1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-26 15:26:19 +00:00

Fix search types support up and down (#6171)

* 01 fix loading icon

* 02 press up and down select
This commit is contained in:
Michael An
2024-06-06 15:37:28 +08:00
committed by GitHub
parent 23027a8b48
commit abcbd0f3dc
2 changed files with 114 additions and 27 deletions

View File

@@ -8,6 +8,7 @@ import SearchResultItem from './search-result-item';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import { isMac } from '../../utils/extra-attributes'; import { isMac } from '../../utils/extra-attributes';
import toaster from '../toast'; import toaster from '../toast';
import Loading from '../loading';
const propTypes = { const propTypes = {
repoID: PropTypes.string, repoID: PropTypes.string,
@@ -20,6 +21,10 @@ const propTypes = {
const PER_PAGE = 10; const PER_PAGE = 10;
const controlKey = isMac() ? '⌘' : 'Ctrl'; const controlKey = isMac() ? '⌘' : 'Ctrl';
const isEnter = isHotkey('enter');
const isUp = isHotkey('up');
const isDown = isHotkey('down');
class Search extends Component { class Search extends Component {
constructor(props) { constructor(props) {
@@ -40,6 +45,8 @@ class Search extends Component {
isCloseShow: false, isCloseShow: false,
isSearchInputShow: false, // for mobile isSearchInputShow: false, // for mobile
searchPageUrl: this.baseSearchPageURL, searchPageUrl: this.baseSearchPageURL,
searchTypesMax: 0,
highlightSearchTypesIndex: 0,
}; };
this.highlightRef = null; this.highlightRef = null;
this.source = null; // used to cancel request; this.source = null; // used to cancel request;
@@ -59,6 +66,7 @@ class Search extends Component {
document.removeEventListener('keydown', this.onDocumentKeydown); document.removeEventListener('keydown', this.onDocumentKeydown);
document.removeEventListener('compositionstart', this.onCompositionStart); document.removeEventListener('compositionstart', this.onCompositionStart);
document.removeEventListener('compositionend', this.onCompositionEnd); document.removeEventListener('compositionend', this.onCompositionEnd);
this.isChineseInput = false;
} }
onCompositionStart = () => { onCompositionStart = () => {
@@ -80,17 +88,18 @@ class Search extends Component {
e.preventDefault(); e.preventDefault();
this.inputRef && this.inputRef.current && this.inputRef.current.blur(); this.inputRef && this.inputRef.current && this.inputRef.current.blur();
this.resetToDefault(); this.resetToDefault();
} else if (isHotkey('enter', e)) { } else if (isEnter(e)) {
this.onEnter(e); this.onEnter(e);
} else if (isHotkey('up', e)) { } else if (isUp(e)) {
this.onUp(e); this.onUp(e);
} else if (isHotkey('down', e)) { } else if (isDown(e)) {
this.onDown(e); this.onDown(e);
} }
}; };
onFocusHandler = () => { onFocusHandler = () => {
this.setState({ width: '570px', isMaskShow: true, isCloseShow: true }); this.setState({ width: '570px', isMaskShow: true, isCloseShow: true });
this.calculateHighlightType();
}; };
onCloseHandler = () => { onCloseHandler = () => {
@@ -100,6 +109,14 @@ class Search extends Component {
onUp = (e) => { onUp = (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (!this.state.isResultGetted) {
let highlightSearchTypesIndex = this.state.highlightSearchTypesIndex - 1;
if (highlightSearchTypesIndex < 0) {
highlightSearchTypesIndex = this.state.searchTypesMax;
}
this.setState({ highlightSearchTypesIndex });
return;
}
const { highlightIndex } = this.state; const { highlightIndex } = this.state;
if (highlightIndex > 0) { if (highlightIndex > 0) {
this.setState({ highlightIndex: highlightIndex - 1 }, () => { this.setState({ highlightIndex: highlightIndex - 1 }, () => {
@@ -116,6 +133,14 @@ class Search extends Component {
onDown = (e) => { onDown = (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
if (!this.state.isResultGetted) {
let highlightSearchTypesIndex = this.state.highlightSearchTypesIndex + 1;
if (highlightSearchTypesIndex > this.state.searchTypesMax) {
highlightSearchTypesIndex = 0;
}
this.setState({ highlightSearchTypesIndex });
return;
}
const { highlightIndex, resultItems } = this.state; const { highlightIndex, resultItems } = this.state;
if (highlightIndex < resultItems.length - 1) { if (highlightIndex < resultItems.length - 1) {
this.setState({ highlightIndex: highlightIndex + 1 }, () => { this.setState({ highlightIndex: highlightIndex + 1 }, () => {
@@ -132,6 +157,21 @@ class Search extends Component {
onEnter = (e) => { onEnter = (e) => {
e.preventDefault(); e.preventDefault();
if (!this.state.isResultGetted) {
let highlightDom = document.querySelector('.search-types-highlight');
if (highlightDom) {
if (highlightDom.classList.contains('search-types-folder')) {
this.searchFolder();
}
else if (highlightDom.classList.contains('search-types-repo')) {
this.searchRepo();
}
else if (highlightDom.classList.contains('search-types-repos')) {
this.searchAllRepos();
}
return;
}
}
let item = this.state.resultItems[this.state.highlightIndex]; let item = this.state.resultItems[this.state.highlightIndex];
if (item) { if (item) {
if (document.activeElement) { if (document.activeElement) {
@@ -147,6 +187,21 @@ class Search extends Component {
this.props.onSearchedClick(item); this.props.onSearchedClick(item);
}; };
calculateHighlightType = () => {
let searchTypesMax = 0;
const { repoID, path } = this.props;
if (repoID) {
searchTypesMax++;
}
if (path && path !== '/') {
searchTypesMax++;
}
this.setState({
searchTypesMax,
highlightSearchTypesIndex: 0,
});
};
keepVisitedItem = (targetItem) => { keepVisitedItem = (targetItem) => {
const { repoID } = this.props; const { repoID } = this.props;
let targetIndex; let targetIndex;
@@ -185,11 +240,10 @@ class Search extends Component {
}, 1); }, 1);
}; };
onKeydownHandler = (event) => { onKeydownHandler = (e) => {
if (isHotkey('enter', event)) { if (!isEnter(e) && !isUp(e) && !isDown(e)) {
this.searchRepo();
} else {
this.setState({ this.setState({
isLoading: false,
highlightIndex: 0, highlightIndex: 0,
resultItems: [], resultItems: [],
isResultGetted: false isResultGetted: false
@@ -202,6 +256,7 @@ class Search extends Component {
this.source.cancel('prev request is cancelled'); this.source.cancel('prev request is cancelled');
} }
this.setState({ this.setState({
isLoading: true,
isResultGetted: false, isResultGetted: false,
resultItems: [], resultItems: [],
highlightIndex: 0, highlightIndex: 0,
@@ -330,7 +385,7 @@ class Search extends Component {
} }
renderSearchResult() { renderSearchResult() {
const { resultItems, width, showRecent, isResultGetted } = this.state; const { resultItems, width, showRecent, isResultGetted, isLoading } = this.state;
if (!width || width === 'default') return null; if (!width || width === 'default') return null;
if (showRecent) { if (showRecent) {
@@ -345,6 +400,9 @@ class Search extends Component {
} }
} }
if (isLoading) {
return <Loading />;
}
if (this.state.inputValue.trim().length === 0) { if (this.state.inputValue.trim().length === 0) {
return <div className="search-result-none">{gettext('Type characters to start search')}</div>; return <div className="search-result-none">{gettext('Type characters to start search')}</div>;
} }
@@ -360,29 +418,56 @@ class Search extends Component {
} }
renderSearchTypes = (inputValue) => { renderSearchTypes = (inputValue) => {
return ( const highlightIndex = this.state.highlightSearchTypesIndex;
<div className="search-types"> if (!this.props.repoID) {
{this.props.repoID && return (
<div className="search-types-repo" onClick={this.searchRepo}> <div className="search-types">
<div className="search-types-repos search-types-highlight" onClick={this.searchAllRepos} tabIndex={0}>
<i className="search-icon-left input-icon-addon fas fa-search"></i> <i className="search-icon-left input-icon-addon fas fa-search"></i>
{inputValue} {inputValue}
<span className="search-types-text">{gettext('in this library')}</span> <span className="search-types-text">{gettext('in all libraries')}</span>
</div> </div>
}
{(this.props.path && this.props.path !== '/') &&
<div className="search-types-folder" onClick={this.searchFolder}>
<i className="search-icon-left input-icon-addon fas fa-search"></i>
{inputValue}
<span className="search-types-text">{gettext('in this folder')}</span>
</div>
}
<div className="search-types-repos" onClick={this.searchAllRepos}>
<i className="search-icon-left input-icon-addon fas fa-search"></i>
{inputValue}
<span className="search-types-text">{gettext('in all libraries')}</span>
</div> </div>
</div> );
); }
if (this.props.repoID) {
if (this.props.path && this.props.path !== '/') {
return (
<div className="search-types">
<div className={`search-types-repo ${highlightIndex === 0 ? 'search-types-highlight' : ''}`} onClick={this.searchRepo} tabIndex={0}>
<i className="search-icon-left input-icon-addon fas fa-search"></i>
{inputValue}
<span className="search-types-text">{gettext('in this library')}</span>
</div>
<div className={`search-types-folder ${highlightIndex === 1 ? 'search-types-highlight' : ''}`} onClick={this.searchFolder} tabIndex={0}>
<i className="search-icon-left input-icon-addon fas fa-search"></i>
{inputValue}
<span className="search-types-text">{gettext('in this folder')}</span>
</div>
<div className={`search-types-repos ${highlightIndex === 2 ? 'search-types-highlight' : ''}`} onClick={this.searchAllRepos} tabIndex={0}>
<i className="search-icon-left input-icon-addon fas fa-search"></i>
{inputValue}
<span className="search-types-text">{gettext('in all libraries')}</span>
</div>
</div>
);
} else {
return (
<div className="search-types">
<div className={`search-types-repo ${highlightIndex === 0 ? 'search-types-highlight' : ''}`} onClick={this.searchRepo} tabIndex={0}>
<i className="search-icon-left input-icon-addon fas fa-search"></i>
{inputValue}
<span className="search-types-text">{gettext('in this library')}</span>
</div>
<div className={`search-types-repos ${highlightIndex === 1 ? 'search-types-highlight' : ''}`} onClick={this.searchAllRepos} tabIndex={0}>
<i className="search-icon-left input-icon-addon fas fa-search"></i>
{inputValue}
<span className="search-types-text">{gettext('in all libraries')}</span>
</div>
</div>
);
}
}
} }
searchRepo = () => { searchRepo = () => {

View File

@@ -400,6 +400,8 @@
border-radius: 4px; border-radius: 4px;
} }
.search-types .search-types-highlight,
.search-types>div:focus,
.search-types>div:hover { .search-types>div:hover {
background-color: rgb(245, 245, 245); background-color: rgb(245, 245, 245);
} }