kopia lustrzana https://github.com/OpenDroneMap/WebODM
191 wiersze
6.7 KiB
JavaScript
191 wiersze
6.7 KiB
JavaScript
import React from 'react';
|
||
import '../css/Paginator.scss';
|
||
import { Link, withRouter } from 'react-router-dom';
|
||
import SortPanel from './SortPanel';
|
||
import Utils from '../classes/Utils';
|
||
import { _ } from '../classes/gettext';
|
||
|
||
let decodeSearch = (search) => {
|
||
return window.decodeURI(search.replace(/:/g, "#"));
|
||
};
|
||
|
||
class Paginator extends React.Component {
|
||
constructor(props){
|
||
super(props);
|
||
|
||
const q = Utils.queryParams(props.location);
|
||
|
||
this.state = {
|
||
searchText: decodeSearch(q.search || ""),
|
||
sortKey: q.ordering || "-created_at"
|
||
}
|
||
|
||
this.sortItems = [{
|
||
key: "created_at",
|
||
label: _("Created on")
|
||
},{
|
||
key: "name",
|
||
label: _("Name")
|
||
},{
|
||
key: "tags",
|
||
label: _("Tags")
|
||
},{
|
||
key: "owner",
|
||
label: _("Owner")
|
||
}];
|
||
}
|
||
|
||
componentDidMount(){
|
||
document.addEventListener("onProjectListTagClicked", this.addTagAndSearch);
|
||
}
|
||
|
||
componentWillUnmount(){
|
||
document.removeEventListener("onProjectListTagClicked", this.addTagAndSearch);
|
||
}
|
||
|
||
closeSearch = () => {
|
||
this.searchContainer.classList.remove("open");
|
||
}
|
||
|
||
toggleSearch = e => {
|
||
e.stopPropagation();
|
||
setTimeout(() => {
|
||
this.searchInput.focus();
|
||
}, 50);
|
||
}
|
||
|
||
handleSearchChange = e => {
|
||
this.setState({searchText: e.target.value});
|
||
}
|
||
|
||
handleSearchKeyDown = e => {
|
||
if (e.key === "Enter"){
|
||
this.search();
|
||
}
|
||
}
|
||
|
||
search = () => {
|
||
this.props.history.push({search: this.getQueryForPage(1)});
|
||
this.closeSearch();
|
||
}
|
||
|
||
clearSearch = () => {
|
||
this.setState({searchText: ""});
|
||
setTimeout(() => {
|
||
this.search();
|
||
}, 0);
|
||
}
|
||
|
||
sortChanged = key => {
|
||
this.setState({sortKey: key});
|
||
setTimeout(() => {
|
||
this.props.history.push({search: this.getQueryForPage(this.props.currentPage)});
|
||
}, 0);
|
||
}
|
||
|
||
getQueryForPage = (num) => {
|
||
return Utils.toSearchQuery({
|
||
page: num,
|
||
ordering: this.state.sortKey,
|
||
search: this.state.searchText.replace(/#/g, ":")
|
||
});
|
||
}
|
||
|
||
addTagAndSearch = e => {
|
||
const tag = e.detail;
|
||
if (tag === undefined) return;
|
||
|
||
let { searchText } = this.state;
|
||
if (searchText === "") searchText += "#" + tag;
|
||
else searchText += " #" + tag;
|
||
|
||
this.setState({searchText});
|
||
setTimeout(() => {
|
||
this.search();
|
||
}, 0);
|
||
}
|
||
|
||
render() {
|
||
const { itemsPerPage, totalItems, currentPage } = this.props;
|
||
const { searchText } = this.state;
|
||
|
||
let paginator = null;
|
||
let clearSearch = null;
|
||
let toolbar = (<ul className={"pagination pagination-sm toolbar " + (totalItems == 0 && !searchText ? "hidden " : " ") + (totalItems / itemsPerPage <= 1 ? "no-margin" : "")}>
|
||
<li className="btn-group" ref={domNode => { this.searchContainer = domNode; }}>
|
||
<a href="javascript:void(0);" className="dropdown-toggle"
|
||
data-toggle-outside
|
||
data-toggle="dropdown"
|
||
aria-haspopup="true" aria-expanded="false"
|
||
onClick={this.toggleSearch}
|
||
title={_("Search")}><i className="fa fa-search"></i></a>
|
||
<ul className="dropdown-menu dropdown-menu-right search-popup">
|
||
<li>
|
||
<input type="text"
|
||
ref={(domNode) => { this.searchInput = domNode}}
|
||
className="form-control search theme-border-secondary-07"
|
||
placeholder={_("Search names or #tags")}
|
||
spellCheck="false"
|
||
autoComplete="false"
|
||
value={searchText}
|
||
onKeyDown={this.handleSearchKeyDown}
|
||
onChange={this.handleSearchChange} />
|
||
<button onClick={this.search} className="btn btn-sm btn-default"><i className="fa fa-search"></i></button>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li className="btn-group">
|
||
<a href="javascript:void(0);" className="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i className="fa fa-sort-alpha-down" title={_("Sort")}></i></a>
|
||
<SortPanel selected={this.state.sortKey} items={this.sortItems} onChange={this.sortChanged} />
|
||
</li>
|
||
</ul>);
|
||
|
||
if (this.props.currentSearch){
|
||
let currentSearch = decodeSearch(this.props.currentSearch);
|
||
clearSearch = (<span className="clear-search">{_("Search results for:")} <span className="query">{currentSearch}</span> <a href="javascript:void(0);" onClick={this.clearSearch}>×</a></span>);
|
||
}
|
||
|
||
if (itemsPerPage && itemsPerPage && totalItems > itemsPerPage){
|
||
const numPages = Math.ceil(totalItems / itemsPerPage);
|
||
const MAX_PAGE_BUTTONS = 7;
|
||
|
||
let rangeStart = Math.max(1, currentPage - Math.floor(MAX_PAGE_BUTTONS / 2));
|
||
let rangeEnd = rangeStart + Math.min(numPages, MAX_PAGE_BUTTONS);
|
||
if (rangeEnd > numPages){
|
||
rangeStart -= rangeEnd - numPages - 1;
|
||
rangeEnd -= rangeEnd - numPages - 1
|
||
}
|
||
let pages = [...Array(rangeEnd - rangeStart).keys()].map(i => i + rangeStart - 1);
|
||
|
||
paginator = (
|
||
<ul className="pagination pagination-sm">
|
||
<li className={currentPage === 1 ? "disabled" : ""}>
|
||
<Link to={{search: this.getQueryForPage(1)}}>
|
||
<span>«</span>
|
||
</Link>
|
||
</li>
|
||
{pages.map(page => {
|
||
return (<li
|
||
key={page + 1}
|
||
className={currentPage === (page + 1) ? "active" : ""}
|
||
><Link to={{search: this.getQueryForPage(page + 1)}}>{page + 1}</Link></li>);
|
||
})}
|
||
<li className={currentPage === numPages ? "disabled" : ""}>
|
||
<Link to={{search: this.getQueryForPage(numPages)}}>
|
||
<span>»</span>
|
||
</Link>
|
||
</li>
|
||
</ul>
|
||
);
|
||
}
|
||
|
||
return [
|
||
<div key="0" className="text-right paginator">{clearSearch}{toolbar}{paginator}</div>,
|
||
this.props.children,
|
||
<div key="2" className="text-right paginator">{paginator}</div>,
|
||
];
|
||
}
|
||
}
|
||
|
||
export default withRouter(Paginator);
|