1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-21 03:18:23 +00:00

change search input (#5661)

This commit is contained in:
Michael An
2023-10-08 17:20:38 +08:00
committed by GitHub
parent f5d47c6d88
commit 4175181688
2 changed files with 137 additions and 86 deletions

View File

@@ -1,10 +1,10 @@
import React, { Component, Fragment } from 'react'; import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import isHotkey from 'is-hotkey';
import MediaQuery from 'react-responsive'; import MediaQuery from 'react-responsive';
import { seafileAPI } from '../../utils/seafile-api'; import { seafileAPI } from '../../utils/seafile-api';
import { gettext, siteRoot, username } from '../../utils/constants'; import { gettext, siteRoot, username } from '../../utils/constants';
import SearchResultItem from './search-result-item'; import SearchResultItem from './search-result-item';
import More from '../more';
import { Utils } from '../../utils/utils'; import { Utils } from '../../utils/utils';
import toaster from '../toast'; import toaster from '../toast';
@@ -15,6 +15,8 @@ const propTypes = {
isPublic: PropTypes.bool, isPublic: PropTypes.bool,
}; };
const PER_PAGE = 10;
class Search extends Component { class Search extends Component {
constructor(props) { constructor(props) {
@@ -24,7 +26,9 @@ class Search extends Component {
width: 'default', width: 'default',
value: '', value: '',
resultItems: [], resultItems: [],
total: 0, page: 0,
isLoading: false,
hasMore: true,
isMaskShow: false, isMaskShow: false,
isResultShow: false, isResultShow: false,
isResultGetted: false, isResultGetted: false,
@@ -34,11 +38,35 @@ class Search extends Component {
}; };
this.inputValue = ''; this.inputValue = '';
this.source = null; // used to cancel request; this.source = null; // used to cancel request;
this.inputRef = React.createRef();
this.searchResultListRef = React.createRef();
} }
componentDidMount() {
document.addEventListener('keydown', this.onDocumentKeydown);
}
componentWillUnmount() {
document.removeEventListener('keydown', this.onDocumentKeydown);
}
onDocumentKeydown = (e) => {
if (isHotkey('mod+f')(e)) {
e.preventDefault();
this.onFocusHandler();
if (this.inputRef && this.inputRef.current) {
this.inputRef.current.focus();
}
}
else if (isHotkey('esc', e)) {
e.preventDefault();
this.resetToDefault();
}
};
onFocusHandler = () => { onFocusHandler = () => {
this.setState({ this.setState({
width: '30rem', width: '570px',
isMaskShow: true, isMaskShow: true,
isCloseShow: true isCloseShow: true
}); });
@@ -64,6 +92,7 @@ class Search extends Component {
if (this.inputValue === '' || _this.getValueLength(this.inputValue) < 3) { if (this.inputValue === '' || _this.getValueLength(this.inputValue) < 3) {
this.setState({ this.setState({
resultItems: [],
isResultShow: false, isResultShow: false,
isResultGetted: false isResultGetted: false
}); });
@@ -84,73 +113,90 @@ class Search extends Component {
}; };
getSearchResult(queryData) { getSearchResult(queryData) {
if (this.source) {
if(this.source){ this.source.cancel('prev request is cancelled');
this.cancelRequest();
} }
this.setState({ this.setState({
isResultShow: true, isResultShow: true,
isResultGetted: false isResultGetted: false,
resultItems: [],
}); });
this.source = seafileAPI.getSource(); this.source = seafileAPI.getSource();
this.sendRequest(queryData, this.source.token); this.sendRequest(queryData, this.source.token, 1);
} }
sendRequest(queryData, cancelToken) { sendRequest = (queryData, cancelToken, page) => {
var _this = this;
let isPublic = this.props.isPublic; let isPublic = this.props.isPublic;
this.queryData = queryData;
if (isPublic) { if (isPublic) {
seafileAPI.searchFilesInPublishedRepo(queryData.search_repo, queryData.q).then(res => { seafileAPI.searchFilesInPublishedRepo(queryData.search_repo, queryData.q, page, PER_PAGE).then(res => {
if (!res.data.total) { this.source = null;
_this.setState({ if (res.data.total > 0) {
resultItems: [], this.setState({
isResultGetted: true resultItems: [...this.state.resultItems, this.formatResultItems(res.data.results)],
isResultGetted: true,
page: page + 1,
isLoading: false,
hasMore: res.data.has_more,
});
} else {
this.setState({
resultItems: [],
isLoading: false,
isResultGetted: true,
hasMore: res.data.has_more,
}); });
_this.source = null;
return;
} }
let items = _this.formatResultItems(res.data.results);
_this.setState({
resultItems: items,
isResultGetted: true
});
_this.source = null;
}).catch(error => { }).catch(error => {
let errMessage = Utils.getErrorMsg(error); let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage); toaster.danger(errMessage);
this.setState({ isLoading: false });
}); });
} else { } else {
this.updateSearchPageURL(queryData); this.updateSearchPageURL(queryData);
seafileAPI.searchFiles(queryData,cancelToken).then(res => { queryData['per_page'] = PER_PAGE;
if (!res.data.total) { queryData['page'] = page;
_this.setState({ seafileAPI.searchFiles(queryData, cancelToken).then(res => {
resultItems: [], this.source = null;
isResultGetted: true if (res.data.total > 0) {
this.setState({
resultItems: [...this.state.resultItems, ...this.formatResultItems(res.data.results)],
isResultGetted: true,
isLoading: false,
page: page + 1,
hasMore: res.data.has_more,
});
} else {
this.setState({
resultItems: [],
isLoading: false,
isResultGetted: true,
hasMore: res.data.has_more,
}); });
_this.source = null;
return;
} }
}).catch(error => {
let items = _this.formatResultItems(res.data.results);
_this.setState({
resultItems: items,
isResultGetted: true
});
_this.source = null;
}).catch(res => {
/* eslint-disable */ /* eslint-disable */
console.log(res); console.log(error);
/* eslint-enable */ /* eslint-enable */
this.setState({ isLoading: false });
}); });
} }
} };
cancelRequest() { onResultListScroll = (e) => {
this.source.cancel('prev request is cancelled'); // Load less than 100 results
} if (!this.state.hasMore || this.state.isLoading || this.state.resultItems.length > 100) {
return;
}
const listPadding = 20;
if (e.target.scrollTop + e.target.clientHeight + listPadding > this.searchResultListRef.current.clientHeight - 10) {
this.setState({isLoading: true}, () => {
this.source = seafileAPI.getSource();
this.sendRequest(this.queryData, this.source.token, this.state.page);
});
}
};
updateSearchPageURL(queryData) { updateSearchPageURL(queryData) {
let params = ''; let params = '';
@@ -179,8 +225,7 @@ class Search extends Component {
formatResultItems(data) { formatResultItems(data) {
let items = []; let items = [];
let length = data.length > 5 ? 5 : data.length; for (let i = 0; i < data.length; i++) {
for (let i = 0; i < length; i++) {
items[i] = {}; items[i] = {};
items[i]['index'] = [i]; items[i]['index'] = [i];
items[i]['name'] = data[i].name; items[i]['name'] = data[i].name;
@@ -209,24 +254,8 @@ class Search extends Component {
}); });
} }
onShowMore = () => {
let repoID = this.props.repoID;
let newValue = this.state.value;
let queryData = {
q: newValue,
search_repo: repoID ? repoID : 'all',
search_ftypes: 'all',
};
let params = '';
for (let key in queryData) {
params += key + '=' + queryData[key] + '&';
}
window.location = siteRoot + 'search/?' + params.slice(0, params.length - 1);
};
renderSearchResult() { renderSearchResult() {
var _this = this; const { resultItems } = this.state;
if (!this.state.isResultShow) { if (!this.state.isResultShow) {
return; return;
} }
@@ -235,25 +264,22 @@ class Search extends Component {
<span className="loading-icon loading-tip"></span> <span className="loading-icon loading-tip"></span>
); );
} }
if (!this.state.resultItems.length) { if (!resultItems.length) {
return ( return (
<div className="search-result-none">{gettext('No results matching.')}</div> <div className="search-result-none">{gettext('No results matching.')}</div>
); );
} }
const { resultItems, total } = this.state;
const isShowMore = total > resultItems.length;
return ( return (
<ul className="search-result-list"> <ul className="search-result-list" ref={this.searchResultListRef}>
{this.state.resultItems.map((item, index) => { {resultItems.map((item, index) => {
return ( return (
<SearchResultItem <SearchResultItem
key={index} key={index}
item={item} item={item}
onItemClickHandler={_this.onItemClickHandler} onItemClickHandler={this.onItemClickHandler}
/> />
); );
})} })}
{isShowMore && <More onShowMore={this.onShowMore} />}
</ul> </ul>
); );
} }
@@ -273,8 +299,8 @@ class Search extends Component {
<Fragment> <Fragment>
<MediaQuery query="(min-width: 768px)"> <MediaQuery query="(min-width: 768px)">
<div className="search"> <div className="search">
<div className={`search-mask ${this.state.isMaskShow ? '' : 'hide'}`} onClick={this.onCloseHandler}></div> <div className={`search-mask ${this.state.isMaskShow ? 'show' : 'hide'}`} onClick={this.onCloseHandler}></div>
<div className="search-container"> <div className={`search-container ${this.state.isMaskShow ? 'show' : ''}`}>
<div className="input-icon"> <div className="input-icon">
<i className="search-icon-left input-icon-addon fas fa-search"></i> <i className="search-icon-left input-icon-addon fas fa-search"></i>
<input <input
@@ -287,15 +313,16 @@ class Search extends Component {
onFocus={this.onFocusHandler} onFocus={this.onFocusHandler}
onChange={this.onChangeHandler} onChange={this.onChangeHandler}
autoComplete="off" autoComplete="off"
ref={this.inputRef}
/> />
{(this.state.isCloseShow && username) && {(this.state.isCloseShow && username) &&
<a href={searchPageUrl} className="search-icon-right input-icon-addon fas fa-external-link-alt search-icon-arrow"></a> <a href={searchPageUrl} className="search-icon-right input-icon-addon fas fa-external-link-alt search-icon-arrow"></a>
} }
{this.state.isCloseShow && {this.state.isCloseShow &&
<button type="button" className="search-icon-right input-icon-addon fas fa-times border-0 bg-transparent" onClick={this.onCloseHandler} aria-label={gettext('Close')}></button> <button type="button" className="search-icon-right input-icon-addon fas fa-times border-0 bg-transparent mr-4" onClick={this.onCloseHandler} aria-label={gettext('Close')}></button>
} }
</div> </div>
<div className="search-result-container dropdown-search-result-container"> <div className="search-result-container dropdown-search-result-container" onScroll={this.onResultListScroll}>
{this.renderSearchResult()} {this.renderSearchResult()}
</div> </div>
</div> </div>
@@ -329,7 +356,7 @@ class Search extends Component {
<button type="button" className="search-icon-right input-icon-addon fas fa-times border-0 bg-transparent" onClick={this.onCloseHandler} aria-label={gettext('Close')}></button> <button type="button" className="search-icon-right input-icon-addon fas fa-times border-0 bg-transparent" onClick={this.onCloseHandler} aria-label={gettext('Close')}></button>
} }
</div> </div>
<div className="search-result-container dropdown-search-result-container"> <div className="search-result-container dropdown-search-result-container" onScroll={this.onResultListScroll}>
{this.renderSearchResult()} {this.renderSearchResult()}
</div> </div>
</div> </div>

View File

@@ -13,6 +13,22 @@
z-index: 2; z-index: 2;
} }
.search-container.show {
display: flex;
flex-direction: column;
position: absolute;
top: 10px;
right: 110px;
min-height: 200px;
border-radius: 3px;
box-shadow: 0 3px 8px 0 rgba(116,129,141,.1);
background-color: #fff;
cursor: default;
overflow: hidden;
width: 600px;
padding: 1rem 0rem 0rem 1rem;
}
.search-icon-left { .search-icon-left {
display: flex; display: flex;
} }
@@ -32,7 +48,7 @@
} }
.search-icon-arrow { .search-icon-arrow {
right: 25px; right: 40px;
left: auto; left: auto;
} }
@@ -42,6 +58,10 @@
font-size: .875rem; font-size: .875rem;
} }
.search-container.show .search-input {
height: 38px;
}
.search-result-container { .search-result-container {
position: absolute; position: absolute;
top: 2rem; top: 2rem;
@@ -50,10 +70,15 @@
background-color: #fff; background-color: #fff;
border-radius: 0 0 3px 3px; border-radius: 0 0 3px 3px;
box-shadow: 0 3px 8px 0 rgba(116, 129, 141, 0.1); box-shadow: 0 3px 8px 0 rgba(116, 129, 141, 0.1);
top: 60px;
} }
.dropdown-search-result-container { .dropdown-search-result-container {
max-height: 300px; max-height: 300px;
overflow: auto; overflow: auto;
position: relative;
top: 0;
box-shadow: none;
} }
.search-result-container .search-result-none { .search-result-container .search-result-none {
@@ -62,7 +87,7 @@
} }
.search-result-container .search-result-list { .search-result-container .search-result-list {
margin: 0; margin: 10px 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
} }
@@ -71,17 +96,17 @@
color: #eb8205!important; color: #eb8205!important;
} }
.search-result-container .search-result-item:hover {
border-left: 2px solid #eb8205;
background-color: #eee;
}
.search-result-container .search-result-item { .search-result-container .search-result-item {
display: flex; display: flex;
padding: 0.25rem 0.5rem; padding: 0.25rem 0;
border-left: 2px solid #fff;
font-size: 0.8125rem; font-size: 0.8125rem;
cursor: pointer; cursor: pointer;
margin-right: 1rem;
}
.search-result-container .search-result-item:hover {
background-color: #f0f0f0;
border-radius: 5px;
} }
.search-result-item .item-img { .search-result-item .item-img {
@@ -197,7 +222,6 @@
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.search-page .search-result-container .search-result-item:hover { .search-page .search-result-container .search-result-item:hover {
border-left: 2px solid #fff;
background-color: inherit; background-color: inherit;
} }
.search-page .search-result-item .item-content { .search-page .search-result-item .item-content {