This commit is contained in:
Daniel Cortes
2020-06-04 04:50:31 -04:00
parent ecb1cb5bfb
commit 56c927d2ea
8 changed files with 957 additions and 207 deletions

View File

@@ -1,26 +1,111 @@
import React, {Fragment} from "react";
import queryString from "query-string";
import {searchArtist} from "../services/search_service";
import {searchArtist, searchDisc, searchSong} from "../services/search_service";
import SearchBar from "./SearchBar";
import Paginate from "./Paginate";
import {Link} from "react-router-dom";
import {Tab, TabList, TabPanel, Tabs} from "react-tabs";
import {ReactComponent as DiscSVG} from "../svg/disc.svg";
const SearchPlaceholder = () => (
<Fragment>
{[...Array(10)].map((e, i) => <li key={i} className='entity'/>)}
</Fragment>
)
class SearchDisc extends React.Component {
render() {
const disc = this.props.disc;
return (
<li className='disc'>
<Link to={`/disc/${disc.id}`}>
{disc.cover_art ? <img src={disc.cover_art.image} className='coverart' alt={`Cover del disco: ${disc.title}`}/> : <DiscSVG className='coverart'/>}
<div class='description'>
<span>{disc.title}</span>
<span className='small'>{disc.artist[0].name}</span>
</div>
</Link>
</li>
)
}
}
class SearchDiscs extends React.Component {
constructor(props) {
super(props);
this.state = {discs: null, paginate: null, page: 1}
}
componentDidMount = _ => this.loadDiscs(this.props.query, this.state.page);
componentDidUpdate = (prevProps, prevState) => {
if (prevProps.query !== this.props.query || prevState.page !== this.state.page) {
this.loadDiscs(this.props.query, this.state.page)
if (prevState.page !== this.state.page) {
this.setState({discs: null})
} else {
this.setState({discs: null, paginate: null})
}
}
};
makeLink = page => `/search/disc?query=${this.state.query}&page=${page}`;
handlePageChange = page => {
this.setState({discs: null, page: page})
this.props.onPageChange(page);
}
loadDiscs = (query, page) => {
page = page ? page : 1;
searchDisc(query, page).then((response) => {
this.setState({
discs: response.releases,
paginate: response.paginate
})
})
};
render = () => {
let discs = <SearchPlaceholder/>;
if (this.state.discs) {
discs = this.state.discs.map((disc) => <SearchDisc key={disc.id} disc={disc}/>);
}
let paginate;
if (this.state.paginate) {
const total = this.state.paginate.total;
const currentPage = this.state.paginate.current_page;
const pageLimit = this.state.paginate.per_page;
paginate = <Paginate totalRecords={total} pageLimit={pageLimit} currentPage={currentPage} pageNeighbours={2} onPageChanged={this.handlePageChange} makeLink={this.makeLink}/>
}
return (
<Fragment>
<ul className='entity_list'>
{discs}
</ul>
{paginate}
</Fragment>
)
};
}
class SearchArtist extends React.Component {
render() {
const artist = this.props.artist;
return (
<li className='entity'>
<li className='artist'>
<Link to={`/artist/${artist.id}`}>
<span>{artist.name}</span>
<span className='small'>{[artist.type, artist.country].filter(Boolean).join(' - ')}</span>
<span className='description'>
<span>{artist.name}</span>
<span className='small'>{[artist.type, artist.country].filter(Boolean).join(' - ')}</span>
</span>
</Link>
</li>
)
@@ -30,18 +115,18 @@ class SearchArtist extends React.Component {
class SearchArtists extends React.Component {
constructor(props) {
super(props);
this.state = { artists: null, paginate: null, page: 1 }
this.state = {artists: null, paginate: null, page: 1}
}
componentDidMount = _ => this.loadArtists(this.props.query, this.state.page);
componentDidUpdate = (prevProps, prevState) => {
if(prevProps.query !== this.props.query || prevState.page !== this.state.page) {
if (prevProps.query !== this.props.query || prevState.page !== this.state.page) {
this.loadArtists(this.props.query, this.state.page)
if(prevState.page !== this.state.page){
if (prevState.page !== this.state.page) {
this.setState({artists: null})
}else{
} else {
this.setState({artists: null, paginate: null})
}
}
@@ -67,12 +152,12 @@ class SearchArtists extends React.Component {
render = () => {
let artists = <SearchPlaceholder/>;
if(this.state.artists) {
artists = this.state.artists.map((artist) => <SearchArtist key={artist.id} artist={artist}/>);
if (this.state.artists) {
artists = this.state.artists.map((artist) => <SearchArtist key={artist.id} artist={artist}/>);
}
let paginate;
if(this.state.paginate){
if (this.state.paginate) {
const total = this.state.paginate.total;
const currentPage = this.state.paginate.current_page;
const pageLimit = this.state.paginate.per_page;
@@ -98,16 +183,16 @@ class SearchTabs extends React.Component {
}
nameToIndex(name) {
if(name === 'artist') return 0
if(name === 'disc') return 1
if(name === 'song') return 2
if (name === 'artist') return 0
if (name === 'disc') return 1
if (name === 'song') return 2
else return 0;
}
indexToName(index){
if(index === 0) return 'artist'
if(index === 1) return 'disc'
if(index === 2) return 'song'
indexToName(index) {
if (index === 0) return 'artist'
if (index === 1) return 'disc'
if (index === 2) return 'song'
else return 'artist';
}
@@ -128,7 +213,7 @@ class SearchTabs extends React.Component {
<Tab className='tab' selectedClassName='selected'>Canciones</Tab>
</TabList>
<TabPanel><SearchArtists query={this.props.query} onPageChange={this.handlePageChange('artist')}/></TabPanel>
<TabPanel><p>Discos</p></TabPanel>
<TabPanel><SearchDiscs query={this.props.query} onPageChange={this.handlePageChange('disc')}/></TabPanel>
<TabPanel><p>Canciones</p></TabPanel>
</Tabs>
)

View File

@@ -4,7 +4,7 @@ import {Search} from './components/Search';
import SearchBar from "./components/SearchBar";
import './styles/reset.css';
import './styles/main.css';
import './styles/main.scss';
import {Nav} from "./components/Nav";

View File

@@ -9,7 +9,7 @@ export async function searchArtist(query, page) {
}
export async function searchDisc(query, page) {
const url = `${baseUrl}/release-group?query=${query}&page=${page}`;
const url = `${baseUrl}/release?query=${query}&page=${page}`;
const response = await axios.get(url);
return response.data
}

View File

@@ -1,183 +0,0 @@
/*Colorscheme*/
:root {
--white: hsl(0, 0%, 99%);
--gray-1: hsl(0, 0%, 95%);
--gray-2: hsl(0, 0%, 85%);
--gray-3: hsl(0, 0%, 30%);
--black: hsl(0, 0%, 20%);
--accent: hsl(354, 81%, 56%);
}
/*Modificacion basica de elementos*/
body {
max-width: 75rem;
margin: 0 auto;
padding: 0 1rem;
font-family: sans-serif;
color: var(--black);
overflow-y: scroll;
}
input {
border: 1px var(--gray-2) solid;
padding: .3rem;
}
button {
border: 1px var(--gray-2) solid;
}
button.link {
display: inline;
border: none;
background-color: inherit;
padding: 0;
cursor: pointer;
}
a, button.link {
color: var(--accent);
text-decoration: none;
}
a:hover, button.link:hover {
text-decoration: underline;
}
h1, h2, h3, h4 {
margin-top: 1rem;
margin-bottom: 1rem;
}
/*Navbar*/
.nav {
display: flex;
min-height: 3.25rem;
position: relative;
justify-content: space-between;
align-items: center;
}
.nav .branding {
margin: 0;
}
.nav-links {
display: flex;
}
.nav-links .link {
margin-left: 1rem;
}
/*Pagination*/
ul.pagination {
display: flex;
margin: 1rem auto;
justify-content: center;
}
.page-item {
border: 1px solid var(--gray-2);
}
.page-link {
color: var(--black);
text-decoration: none;
padding: 0 1rem;
}
@media (max-width: 767px) {
.page-link {
padding: 0 .4rem;
}
}
.page-item.active {
background-color: var(--accent);
}
.page-item.active a {
color: white;
}
/*Input with icon*/
.input-with-icon {
display: flex;
}
.input-with-icon input {
border-right: none;
}
.input-with-icon button {
border-left: none;
background: white;
}
/*Lista de entidades*/
ul.entity_list {
display: flex;
flex-direction: column;
}
li.entity {
height: 4rem;
border: 1px var(--gray-2) solid;
}
li.entity:not(:first-child) {
border-top: none;
}
li.entity:hover {
background-color: var(--gray-1)
}
li.entity a {
display: flex;
height: 100%;
padding-left: 1rem;
text-decoration: none;
color: var(--black);
justify-content: center;
flex-direction: column;
}
li.entity a .small {
font-size: .8rem;
color: var(--gray-3);
}
/* Tabs */
ul.tabs {
display: flex;
align-items: stretch;
border-bottom: 2px var(--gray-1) solid;
margin: 1rem 0;
}
ul.tabs li.tab {
padding: .5rem 1rem;
margin-bottom: -2px;
cursor: pointer;
}
ul.tabs li.tab:hover {
border-bottom: 2px var(--gray-2) solid;
}
ul.tabs li.tab.selected {
border-bottom: 2px var(--accent) solid;
}
/*Utils*/
.full-width {
width: 100%;
}

209
src/styles/main.scss Normal file
View File

@@ -0,0 +1,209 @@
/*Colorscheme*/
:root {
--white: hsl(0, 0%, 99%);
--gray-1: hsl(0, 0%, 95%);
--gray-2: hsl(0, 0%, 85%);
--gray-3: hsl(0, 0%, 30%);
--black: hsl(0, 0%, 20%);
--accent: hsl(354, 81%, 56%);
}
/*Modificacion basica de elementos*/
body {
max-width: 75rem;
margin: 0 auto;
padding: 0 1rem;
font-family: sans-serif;
color: var(--black);
overflow-y: scroll;
}
input {
border: 1px var(--gray-2) solid;
padding: .3rem;
}
button {
border: 1px var(--gray-2) solid;
}
button.link {
display: inline;
border: none;
background-color: inherit;
padding: 0;
cursor: pointer;
}
a, button.link {
color: var(--accent);
text-decoration: none;
}
a:hover, button.link:hover {
text-decoration: underline;
}
h1, h2, h3, h4 {
margin-top: 1rem;
margin-bottom: 1rem;
}
img {
}
/*Navbar*/
.nav {
display: flex;
min-height: 3.25rem;
position: relative;
justify-content: space-between;
align-items: center;
}
.nav .branding {
margin: 0;
}
.nav-links {
display: flex;
}
.nav-links .link {
margin-left: 1rem;
}
/*Pagination*/
ul.pagination {
display: flex;
margin: 1rem auto;
justify-content: center;
}
.page-item {
border: 1px solid var(--gray-2);
}
.page-link {
color: var(--black);
text-decoration: none;
padding: 0 1rem;
}
@media (max-width: 767px) {
.page-link {
padding: 0 .4rem;
}
}
.page-item.active {
background-color: var(--accent);
}
.page-item.active a {
color: white;
}
/*Input with icon*/
.input-with-icon {
display: flex;
}
.input-with-icon input {
border-right: none;
}
.input-with-icon button {
border-left: none;
background: white;
}
/*Lista de entidades*/
ul.entity_list {
display: flex;
flex-direction: column;
li.artist, li.disc {
border: 1px var(--gray-2) solid;
&:not(:first-child) {
border-top: none;
}
&:hover {
background-color: var(--gray-1)
}
a {
display: flex;
text-decoration: none;
color: var(--black);
align-items: center;
.description {
display: flex;
flex-direction: column;
justify-content: center;
padding-left: 1rem;
.small {
color: var(--gray-3);
font-size: .8rem;
}
}
}
}
li.artist {
a {
height: 4rem;
}
}
li.disc {
a {
height: 14rem;
.coverart {
object-fit: cover;
width: 200px;
height: 200px;
margin: 1rem;
}
}
}
}
/* Tabs */
ul.tabs {
display: flex;
align-items: stretch;
border-bottom: 2px var(--gray-1) solid;
margin: 1rem 0;
}
ul.tabs li.tab {
padding: .5rem 1rem;
margin-bottom: -2px;
cursor: pointer;
}
ul.tabs li.tab:hover {
border-bottom: 2px var(--gray-2) solid;
}
ul.tabs li.tab.selected {
border-bottom: 2px var(--accent) solid;
}
/*Utils*/
.full-width {
width: 100%;
}

7
src/svg/disc.svg Normal file
View File

@@ -0,0 +1,7 @@
<svg version="1.1" width="450" height="450" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="rgb(230, 230, 230)"/>
<circle cx="50%" cy="50%" r="45%" fill="rgb(250, 250, 250)"/>
<circle cx="50%" cy="50%" r="13%" fill="rgb(230, 230, 230)"/>
<circle cx="50%" cy="50%" r="12%" fill="rgb(250, 250, 250)"/>
<circle cx="50%" cy="50%" r="8%" fill="rgb(230, 230, 230)"/>
</svg>

After

Width:  |  Height:  |  Size: 416 B