Me cambie a react~~
This commit is contained in:
177
src/components/Paginate.jsx
Normal file
177
src/components/Paginate.jsx
Normal file
@@ -0,0 +1,177 @@
|
||||
import React, {Component, Fragment} from 'react';
|
||||
|
||||
const LEFT_PAGE = 'LEFT';
|
||||
const RIGHT_PAGE = 'RIGHT';
|
||||
|
||||
/**
|
||||
* Helper method for creating a range of numbers
|
||||
* range(1, 5) => [1, 2, 3, 4, 5]
|
||||
*/
|
||||
const range = (from, to, step = 1) => {
|
||||
let i = from;
|
||||
const range = [];
|
||||
|
||||
while (i <= to) {
|
||||
range.push(i);
|
||||
i += step;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
export class Paginate extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const {totalRecords = null, pageLimit = 30, pageNeighbours = 0} = props;
|
||||
|
||||
this.pageLimit = typeof pageLimit === 'number' ? pageLimit : 30;
|
||||
this.totalRecords = typeof totalRecords === 'number' ? totalRecords : 0;
|
||||
this.pageNeighbours = typeof pageNeighbours === 'number' ? Math.max(0, Math.min(pageNeighbours, 2)) : 0;
|
||||
this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
|
||||
this.state = {currentPage: 1};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
//this.gotoPage(1);
|
||||
}
|
||||
|
||||
gotoPage = page => {
|
||||
const {onPageChanged = f => f} = this.props;
|
||||
const currentPage = Math.max(0, Math.min(page, this.totalPages));
|
||||
this.setState({currentPage}, () => onPageChanged(this.makePageLink(currentPage)));
|
||||
}
|
||||
|
||||
handleClick = page => evt => {
|
||||
evt.preventDefault();
|
||||
this.gotoPage(page);
|
||||
}
|
||||
|
||||
handleMoveLeft = evt => {
|
||||
evt.preventDefault();
|
||||
this.gotoPage(this.state.currentPage - (this.pageNeighbours * 2) - 1);
|
||||
}
|
||||
|
||||
handleMoveRight = evt => {
|
||||
evt.preventDefault();
|
||||
this.gotoPage(this.state.currentPage + (this.pageNeighbours * 2) + 1);
|
||||
}
|
||||
|
||||
makePageLink = page => {
|
||||
const {makeLink = f => f} = this.props;
|
||||
return makeLink(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* Let's say we have 10 pages and we set pageNeighbours to 2
|
||||
* Given that the current page is 6
|
||||
* The pagination control will look like the following:
|
||||
*
|
||||
* (1) < {4 5} [6] {7 8} > (10)
|
||||
*
|
||||
* (x) => terminal pages: first and last page(always visible)
|
||||
* [x] => represents current page
|
||||
* {...x} => represents page neighbours
|
||||
*/
|
||||
fetchPageNumbers = () => {
|
||||
const totalPages = this.totalPages;
|
||||
const currentPage = this.state.currentPage;
|
||||
const pageNeighbours = this.pageNeighbours;
|
||||
|
||||
/**
|
||||
* totalNumbers: the total page numbers to show on the control
|
||||
* totalBlocks: totalNumbers + 2 to cover for the left(<) and right(>) controls
|
||||
*/
|
||||
const totalNumbers = (this.pageNeighbours * 2) + 3;
|
||||
const totalBlocks = totalNumbers + 2;
|
||||
|
||||
if (totalPages > totalBlocks) {
|
||||
|
||||
const startPage = Math.max(2, currentPage - pageNeighbours);
|
||||
const endPage = Math.min(totalPages - 1, currentPage + pageNeighbours);
|
||||
|
||||
let pages = range(startPage, endPage);
|
||||
|
||||
/**
|
||||
* hasLeftSpill: has hidden pages to the left
|
||||
* hasRightSpill: has hidden pages to the right
|
||||
* spillOffset: number of hidden pages either to the left or to the right
|
||||
*/
|
||||
const hasLeftSpill = startPage > 2;
|
||||
const hasRightSpill = (totalPages - endPage) > 1;
|
||||
const spillOffset = totalNumbers - (pages.length + 1);
|
||||
|
||||
switch (true) {
|
||||
// handle: (1) < {5 6} [7] {8 9} (10)
|
||||
case (hasLeftSpill && !hasRightSpill): {
|
||||
const extraPages = range(startPage - spillOffset, startPage - 1);
|
||||
pages = [LEFT_PAGE, ...extraPages, ...pages];
|
||||
break;
|
||||
}
|
||||
|
||||
// handle: (1) {2 3} [4] {5 6} > (10)
|
||||
case (!hasLeftSpill && hasRightSpill): {
|
||||
const extraPages = range(endPage + 1, endPage + spillOffset);
|
||||
pages = [...pages, ...extraPages, RIGHT_PAGE];
|
||||
break;
|
||||
}
|
||||
|
||||
// handle: (1) < {4 5} [6] {7 8} > (10)
|
||||
case (hasLeftSpill && hasRightSpill):
|
||||
default: {
|
||||
pages = [LEFT_PAGE, ...pages, RIGHT_PAGE];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [1, ...pages, totalPages];
|
||||
|
||||
}
|
||||
|
||||
return range(1, totalPages);
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.totalRecords || this.totalPages === 1) return null;
|
||||
|
||||
const {currentPage} = this.state;
|
||||
const pages = this.fetchPageNumbers();
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<nav aria-label="Countries Pagination">
|
||||
<ul className="pagination">
|
||||
{pages.map((page, index) => {
|
||||
if (page === LEFT_PAGE) return (
|
||||
<li key={index} className="page-item">
|
||||
<a className="page-link" href={this.makePageLink(index)} aria-label="Previous"
|
||||
onClick={this.handleMoveLeft}>
|
||||
<span aria-hidden="true">«</span>
|
||||
<span className="sr-only">Previous</span>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
if (page === RIGHT_PAGE) return (
|
||||
<li key={index} className="page-item">
|
||||
<a className="page-link" href={this.makePageLink(index)} aria-label="Next"
|
||||
onClick={this.handleMoveRight}>
|
||||
<span aria-hidden="true">»</span>
|
||||
<span className="sr-only">Next</span>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
return (
|
||||
<li key={index} className={`page-item${currentPage === page ? ' active' : ''}`}>
|
||||
<a className="page-link" href={this.makePageLink(index)}
|
||||
onClick={this.handleClick(page)}>{page}</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
108
src/components/Search.jsx
Normal file
108
src/components/Search.jsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import React from "react";
|
||||
import queryString from "query-string";
|
||||
import ReactJson from "react-json-view";
|
||||
import {searchArtist} from "../services/search_service";
|
||||
import SearchBar from "./SearchBar";
|
||||
import {Paginate} from "./Paginate";
|
||||
import {navigate} from "@reach/router";
|
||||
|
||||
|
||||
class SearchList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.buildList(this.props.artists);
|
||||
}
|
||||
|
||||
buildList(artists) {
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class Search extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
artists: null
|
||||
};
|
||||
|
||||
this.getParams = this.getParams.bind(this);
|
||||
this.makeLink = this.makeLink.bind(this);
|
||||
this.handlePageChange = this.handlePageChange.bind(this);
|
||||
}
|
||||
|
||||
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
if (props.location.search !== state.prevSearch) {
|
||||
return {
|
||||
artists: null,
|
||||
prevSearch: props.location.search
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadArtists(this.getParams().query, this.getParams().page);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
if (this.state.artists == null) {
|
||||
this.loadArtists(this.getParams().query, this.getParams().page);
|
||||
}
|
||||
}
|
||||
|
||||
makeLink(page) {
|
||||
return `/search?query=${this.getParams().query}&page=${page}`
|
||||
}
|
||||
|
||||
handlePageChange(page) {
|
||||
navigate(page);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.artists) {
|
||||
const total = this.state.artists.paginate.total;
|
||||
const pageLimit = this.state.artists.paginate.per_page;
|
||||
|
||||
return (
|
||||
<main>
|
||||
<h1>Busqueda</h1>
|
||||
<SearchBar query={this.getParams().query}/>
|
||||
<SearchList artists={this.state.artists.artists}/>
|
||||
<Paginate totalRecords={total} pageLimit={pageLimit} pageNeighbours={1} onPageChanged={this.handlePageChange} makeLink={this.makeLink}/>
|
||||
<ReactJson src={this.state.artists} enableClipboard={false} displayDataTypes={false}/>
|
||||
</main>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<main>
|
||||
<h1>Busqueda</h1>
|
||||
<SearchBar query={this.query}/>
|
||||
<p>Loading...</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getParams() {
|
||||
return queryString.parse(this.props.location.search);
|
||||
}
|
||||
|
||||
loadArtists(query, page) {
|
||||
if(!page){
|
||||
page = 1;
|
||||
}
|
||||
|
||||
searchArtist(query, page).then((response) => {
|
||||
this.setState({artists: response})
|
||||
})
|
||||
}
|
||||
}
|
||||
38
src/components/SearchBar.jsx
Normal file
38
src/components/SearchBar.jsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from "react";
|
||||
import {FaSearch} from 'react-icons/fa';
|
||||
import {navigate} from "@reach/router";
|
||||
|
||||
export default class SearchBar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {query: ''};
|
||||
|
||||
if (this.props.query) {
|
||||
this.state = {query: this.props.query};
|
||||
}
|
||||
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
handleChange(event) {
|
||||
const query = event.target.value;
|
||||
this.setState({query: query});
|
||||
}
|
||||
|
||||
handleSubmit(event, who) {
|
||||
if (event.key === 'Enter' || who === 'button') {
|
||||
navigate(`/search?query=${this.state.query}`);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='input-with-icon'>
|
||||
<input className='full-width' value={this.state.query} onKeyUp={this.handleSubmit}
|
||||
onChange={this.handleChange}/>
|
||||
<button onClick={(e) => this.handleSubmit(e, 'button')}><FaSearch/></button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user