Discos!
This commit is contained in:
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
209
src/styles/main.scss
Normal 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
7
src/svg/disc.svg
Normal 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 |
Reference in New Issue
Block a user