diff --git a/frontend/src/components/search/search.js b/frontend/src/components/search/search.js index 3e5a4d61a8..a33cd8caa7 100644 --- a/frontend/src/components/search/search.js +++ b/frontend/src/components/search/search.js @@ -8,6 +8,7 @@ import SearchResultItem from './search-result-item'; import { Utils } from '../../utils/utils'; import { isMac } from '../../utils/extra-attributes'; import toaster from '../toast'; +import Loading from '../loading'; const propTypes = { repoID: PropTypes.string, @@ -20,6 +21,10 @@ const propTypes = { const PER_PAGE = 10; const controlKey = isMac() ? '⌘' : 'Ctrl'; +const isEnter = isHotkey('enter'); +const isUp = isHotkey('up'); +const isDown = isHotkey('down'); + class Search extends Component { constructor(props) { @@ -40,6 +45,8 @@ class Search extends Component { isCloseShow: false, isSearchInputShow: false, // for mobile searchPageUrl: this.baseSearchPageURL, + searchTypesMax: 0, + highlightSearchTypesIndex: 0, }; this.highlightRef = null; this.source = null; // used to cancel request; @@ -59,6 +66,7 @@ class Search extends Component { document.removeEventListener('keydown', this.onDocumentKeydown); document.removeEventListener('compositionstart', this.onCompositionStart); document.removeEventListener('compositionend', this.onCompositionEnd); + this.isChineseInput = false; } onCompositionStart = () => { @@ -80,17 +88,18 @@ class Search extends Component { e.preventDefault(); this.inputRef && this.inputRef.current && this.inputRef.current.blur(); this.resetToDefault(); - } else if (isHotkey('enter', e)) { + } else if (isEnter(e)) { this.onEnter(e); - } else if (isHotkey('up', e)) { + } else if (isUp(e)) { this.onUp(e); - } else if (isHotkey('down', e)) { + } else if (isDown(e)) { this.onDown(e); } }; onFocusHandler = () => { this.setState({ width: '570px', isMaskShow: true, isCloseShow: true }); + this.calculateHighlightType(); }; onCloseHandler = () => { @@ -100,6 +109,14 @@ class Search extends Component { onUp = (e) => { e.preventDefault(); 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; if (highlightIndex > 0) { this.setState({ highlightIndex: highlightIndex - 1 }, () => { @@ -116,6 +133,14 @@ class Search extends Component { onDown = (e) => { e.preventDefault(); 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; if (highlightIndex < resultItems.length - 1) { this.setState({ highlightIndex: highlightIndex + 1 }, () => { @@ -132,6 +157,21 @@ class Search extends Component { onEnter = (e) => { 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]; if (item) { if (document.activeElement) { @@ -147,6 +187,21 @@ class Search extends Component { 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) => { const { repoID } = this.props; let targetIndex; @@ -185,11 +240,10 @@ class Search extends Component { }, 1); }; - onKeydownHandler = (event) => { - if (isHotkey('enter', event)) { - this.searchRepo(); - } else { + onKeydownHandler = (e) => { + if (!isEnter(e) && !isUp(e) && !isDown(e)) { this.setState({ + isLoading: false, highlightIndex: 0, resultItems: [], isResultGetted: false @@ -202,6 +256,7 @@ class Search extends Component { this.source.cancel('prev request is cancelled'); } this.setState({ + isLoading: true, isResultGetted: false, resultItems: [], highlightIndex: 0, @@ -330,7 +385,7 @@ class Search extends Component { } renderSearchResult() { - const { resultItems, width, showRecent, isResultGetted } = this.state; + const { resultItems, width, showRecent, isResultGetted, isLoading } = this.state; if (!width || width === 'default') return null; if (showRecent) { @@ -345,6 +400,9 @@ class Search extends Component { } } + if (isLoading) { + return ; + } if (this.state.inputValue.trim().length === 0) { return
{gettext('Type characters to start search')}
; } @@ -360,29 +418,56 @@ class Search extends Component { } renderSearchTypes = (inputValue) => { - return ( -
- {this.props.repoID && -
+ const highlightIndex = this.state.highlightSearchTypesIndex; + if (!this.props.repoID) { + return ( +
+
{inputValue} - {gettext('in this library')} + {gettext('in all libraries')}
- } - {(this.props.path && this.props.path !== '/') && -
- - {inputValue} - {gettext('in this folder')} -
- } -
- - {inputValue} - {gettext('in all libraries')}
-
- ); + ); + } + if (this.props.repoID) { + if (this.props.path && this.props.path !== '/') { + return ( +
+
+ + {inputValue} + {gettext('in this library')} +
+
+ + {inputValue} + {gettext('in this folder')} +
+
+ + {inputValue} + {gettext('in all libraries')} +
+
+ ); + } else { + return ( +
+
+ + {inputValue} + {gettext('in this library')} +
+
+ + {inputValue} + {gettext('in all libraries')} +
+
+ ); + } + } } searchRepo = () => { diff --git a/frontend/src/css/search.css b/frontend/src/css/search.css index ea02dbee23..7d089c03ff 100644 --- a/frontend/src/css/search.css +++ b/frontend/src/css/search.css @@ -400,6 +400,8 @@ border-radius: 4px; } +.search-types .search-types-highlight, +.search-types>div:focus, .search-types>div:hover { background-color: rgb(245, 245, 245); }