1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-20 19:08:21 +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 PropTypes from 'prop-types';
import isHotkey from 'is-hotkey';
import MediaQuery from 'react-responsive';
import { seafileAPI } from '../../utils/seafile-api';
import { gettext, siteRoot, username } from '../../utils/constants';
import SearchResultItem from './search-result-item';
import More from '../more';
import { Utils } from '../../utils/utils';
import toaster from '../toast';
@@ -15,6 +15,8 @@ const propTypes = {
isPublic: PropTypes.bool,
};
const PER_PAGE = 10;
class Search extends Component {
constructor(props) {
@@ -24,7 +26,9 @@ class Search extends Component {
width: 'default',
value: '',
resultItems: [],
total: 0,
page: 0,
isLoading: false,
hasMore: true,
isMaskShow: false,
isResultShow: false,
isResultGetted: false,
@@ -34,11 +38,35 @@ class Search extends Component {
};
this.inputValue = '';
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 = () => {
this.setState({
width: '30rem',
width: '570px',
isMaskShow: true,
isCloseShow: true
});
@@ -64,6 +92,7 @@ class Search extends Component {
if (this.inputValue === '' || _this.getValueLength(this.inputValue) < 3) {
this.setState({
resultItems: [],
isResultShow: false,
isResultGetted: false
});
@@ -84,73 +113,90 @@ class Search extends Component {
};
getSearchResult(queryData) {
if(this.source){
this.cancelRequest();
if (this.source) {
this.source.cancel('prev request is cancelled');
}
this.setState({
isResultShow: true,
isResultGetted: false
isResultGetted: false,
resultItems: [],
});
this.source = seafileAPI.getSource();
this.sendRequest(queryData, this.source.token);
this.sendRequest(queryData, this.source.token, 1);
}
sendRequest(queryData, cancelToken) {
var _this = this;
sendRequest = (queryData, cancelToken, page) => {
let isPublic = this.props.isPublic;
this.queryData = queryData;
if (isPublic) {
seafileAPI.searchFilesInPublishedRepo(queryData.search_repo, queryData.q).then(res => {
if (!res.data.total) {
_this.setState({
resultItems: [],
isResultGetted: true
seafileAPI.searchFilesInPublishedRepo(queryData.search_repo, queryData.q, page, PER_PAGE).then(res => {
this.source = null;
if (res.data.total > 0) {
this.setState({
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 => {
let errMessage = Utils.getErrorMsg(error);
toaster.danger(errMessage);
this.setState({ isLoading: false });
});
} else {
this.updateSearchPageURL(queryData);
seafileAPI.searchFiles(queryData,cancelToken).then(res => {
if (!res.data.total) {
_this.setState({
resultItems: [],
isResultGetted: true
queryData['per_page'] = PER_PAGE;
queryData['page'] = page;
seafileAPI.searchFiles(queryData, cancelToken).then(res => {
this.source = null;
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;
}
let items = _this.formatResultItems(res.data.results);
_this.setState({
resultItems: items,
isResultGetted: true
});
_this.source = null;
}).catch(res => {
}).catch(error => {
/* eslint-disable */
console.log(res);
console.log(error);
/* eslint-enable */
this.setState({ isLoading: false });
});
}
}
};
cancelRequest() {
this.source.cancel('prev request is cancelled');
}
onResultListScroll = (e) => {
// 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) {
let params = '';
@@ -179,8 +225,7 @@ class Search extends Component {
formatResultItems(data) {
let items = [];
let length = data.length > 5 ? 5 : data.length;
for (let i = 0; i < length; i++) {
for (let i = 0; i < data.length; i++) {
items[i] = {};
items[i]['index'] = [i];
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() {
var _this = this;
const { resultItems } = this.state;
if (!this.state.isResultShow) {
return;
}
@@ -235,25 +264,22 @@ class Search extends Component {
<span className="loading-icon loading-tip"></span>
);
}
if (!this.state.resultItems.length) {
if (!resultItems.length) {
return (
<div className="search-result-none">{gettext('No results matching.')}</div>
);
}
const { resultItems, total } = this.state;
const isShowMore = total > resultItems.length;
return (
<ul className="search-result-list">
{this.state.resultItems.map((item, index) => {
<ul className="search-result-list" ref={this.searchResultListRef}>
{resultItems.map((item, index) => {
return (
<SearchResultItem
key={index}
item={item}
onItemClickHandler={_this.onItemClickHandler}
onItemClickHandler={this.onItemClickHandler}
/>
);
})}
{isShowMore && <More onShowMore={this.onShowMore} />}
</ul>
);
}
@@ -273,8 +299,8 @@ class Search extends Component {
<Fragment>
<MediaQuery query="(min-width: 768px)">
<div className="search">
<div className={`search-mask ${this.state.isMaskShow ? '' : 'hide'}`} onClick={this.onCloseHandler}></div>
<div className="search-container">
<div className={`search-mask ${this.state.isMaskShow ? 'show' : 'hide'}`} onClick={this.onCloseHandler}></div>
<div className={`search-container ${this.state.isMaskShow ? 'show' : ''}`}>
<div className="input-icon">
<i className="search-icon-left input-icon-addon fas fa-search"></i>
<input
@@ -287,15 +313,16 @@ class Search extends Component {
onFocus={this.onFocusHandler}
onChange={this.onChangeHandler}
autoComplete="off"
ref={this.inputRef}
/>
{(this.state.isCloseShow && username) &&
<a href={searchPageUrl} className="search-icon-right input-icon-addon fas fa-external-link-alt search-icon-arrow"></a>
}
{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 className="search-result-container dropdown-search-result-container">
<div className="search-result-container dropdown-search-result-container" onScroll={this.onResultListScroll}>
{this.renderSearchResult()}
</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>
}
</div>
<div className="search-result-container dropdown-search-result-container">
<div className="search-result-container dropdown-search-result-container" onScroll={this.onResultListScroll}>
{this.renderSearchResult()}
</div>
</div>