parent
e2a4d8bc24
commit
fba3d73484
10 changed files with 303 additions and 48 deletions
@ -0,0 +1,101 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import MuiAppBar from '@material-ui/core/AppBar'; |
||||||
|
import Toolbar from '@material-ui/core/Toolbar'; |
||||||
|
import IconButton from '@material-ui/core/IconButton'; |
||||||
|
import Typography from '@material-ui/core/Typography'; |
||||||
|
import InputBase from '@material-ui/core/InputBase'; |
||||||
|
import { createStyles, fade, Theme, makeStyles } from '@material-ui/core/styles'; |
||||||
|
import MenuIcon from '@material-ui/icons/Menu'; |
||||||
|
import SearchIcon from '@material-ui/icons/Search'; |
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => |
||||||
|
createStyles({ |
||||||
|
root: { |
||||||
|
flexGrow: 1, |
||||||
|
}, |
||||||
|
menuButton: { |
||||||
|
marginRight: theme.spacing(2), |
||||||
|
}, |
||||||
|
title: { |
||||||
|
flexGrow: 1, |
||||||
|
display: 'none', |
||||||
|
[theme.breakpoints.up('sm')]: { |
||||||
|
display: 'block', |
||||||
|
}, |
||||||
|
}, |
||||||
|
search: { |
||||||
|
position: 'relative', |
||||||
|
borderRadius: theme.shape.borderRadius, |
||||||
|
backgroundColor: fade(theme.palette.common.white, 0.15), |
||||||
|
'&:hover': { |
||||||
|
backgroundColor: fade(theme.palette.common.white, 0.25), |
||||||
|
}, |
||||||
|
marginLeft: 0, |
||||||
|
width: '100%', |
||||||
|
[theme.breakpoints.up('sm')]: { |
||||||
|
marginLeft: theme.spacing(1), |
||||||
|
width: 'auto', |
||||||
|
}, |
||||||
|
}, |
||||||
|
searchIcon: { |
||||||
|
padding: theme.spacing(0, 2), |
||||||
|
height: '100%', |
||||||
|
position: 'absolute', |
||||||
|
pointerEvents: 'none', |
||||||
|
display: 'flex', |
||||||
|
alignItems: 'center', |
||||||
|
justifyContent: 'center', |
||||||
|
}, |
||||||
|
inputRoot: { |
||||||
|
color: 'inherit', |
||||||
|
}, |
||||||
|
inputInput: { |
||||||
|
padding: theme.spacing(1, 1, 1, 0), |
||||||
|
// vertical padding + font size from searchIcon
|
||||||
|
paddingLeft: `calc(1em + ${theme.spacing(4)}px)`, |
||||||
|
transition: theme.transitions.create('width'), |
||||||
|
width: '100%', |
||||||
|
[theme.breakpoints.up('sm')]: { |
||||||
|
width: '12ch', |
||||||
|
'&:focus': { |
||||||
|
width: '20ch', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}), |
||||||
|
); |
||||||
|
|
||||||
|
export default function AppBar() { |
||||||
|
const classes = useStyles(); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className={classes.root}> |
||||||
|
<MuiAppBar position="static"> |
||||||
|
<Toolbar> |
||||||
|
<IconButton |
||||||
|
edge="start" |
||||||
|
className={classes.menuButton} |
||||||
|
color="inherit" |
||||||
|
aria-label="open drawer" |
||||||
|
> |
||||||
|
<MenuIcon /> |
||||||
|
</IconButton> |
||||||
|
<Typography className={classes.title} variant="h6" noWrap>MuDBase</Typography> |
||||||
|
<div className={classes.search}> |
||||||
|
<div className={classes.searchIcon}> |
||||||
|
<SearchIcon /> |
||||||
|
</div> |
||||||
|
<InputBase |
||||||
|
placeholder="Search…" |
||||||
|
classes={{ |
||||||
|
root: classes.inputRoot, |
||||||
|
input: classes.inputInput, |
||||||
|
}} |
||||||
|
inputProps={{ 'aria-label': 'search' }} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</Toolbar> |
||||||
|
</MuiAppBar> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'; |
||||||
|
import List from '@material-ui/core/List'; |
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => |
||||||
|
createStyles({ |
||||||
|
root: { |
||||||
|
flexGrow: 1, |
||||||
|
maxWidth: 752, |
||||||
|
}, |
||||||
|
}), |
||||||
|
); |
||||||
|
|
||||||
|
export default function ItemList(props:any) { |
||||||
|
const classes = useStyles(); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className={classes.root}> |
||||||
|
<List dense={true}> |
||||||
|
{props.children} |
||||||
|
</List> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { DisplayItem, isSong, isLoadingSong } from '../types/DisplayItem'; |
||||||
|
import ItemListLoadedSongItem from './ItemListLoadedSongItem'; |
||||||
|
import ItemListLoadingSongItem from './ItemListLoadingSongItem'; |
||||||
|
|
||||||
|
export interface IProps { |
||||||
|
item: DisplayItem |
||||||
|
} |
||||||
|
|
||||||
|
export default function ItemListItem(props: IProps) { |
||||||
|
return <> |
||||||
|
{isSong(props.item) && <ItemListLoadedSongItem item={props.item}/>} |
||||||
|
{isLoadingSong(props.item) && <ItemListLoadingSongItem item={props.item}/>} |
||||||
|
</> |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import ListItem from '@material-ui/core/ListItem'; |
||||||
|
import ListItemIcon from '@material-ui/core/ListItemIcon'; |
||||||
|
import ListItemText from '@material-ui/core/ListItemText'; |
||||||
|
import MusicNoteIcon from '@material-ui/icons/MusicNote'; |
||||||
|
|
||||||
|
import { SongDisplayItem } from '../types/DisplayItem'; |
||||||
|
|
||||||
|
export interface IProps { |
||||||
|
item: SongDisplayItem |
||||||
|
} |
||||||
|
|
||||||
|
export default function ItemListLoadedSongItem(props: IProps) { |
||||||
|
var artists = props.item.artistNames.length ? props.item.artistNames[0] : "Unknown"; |
||||||
|
for(var i:number=1; i<props.item.artistNames.length; i++) { |
||||||
|
artists = artists.concat(", " + props.item.artistNames[i]); |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<ListItem> |
||||||
|
<ListItemIcon> |
||||||
|
<MusicNoteIcon /> |
||||||
|
</ListItemIcon> |
||||||
|
<ListItemText |
||||||
|
primary={props.item.title} |
||||||
|
secondary={artists} |
||||||
|
/> |
||||||
|
</ListItem> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import ListItem from '@material-ui/core/ListItem'; |
||||||
|
import ListItemIcon from '@material-ui/core/ListItemIcon'; |
||||||
|
import MusicNoteIcon from '@material-ui/icons/MusicNote'; |
||||||
|
import CircularProgress from '@material-ui/core/CircularProgress'; |
||||||
|
|
||||||
|
import { LoadingSongDisplayItem } from '../types/DisplayItem'; |
||||||
|
|
||||||
|
export interface IProps { |
||||||
|
item: LoadingSongDisplayItem |
||||||
|
} |
||||||
|
|
||||||
|
export default function ItemListLoadingSongItem(props: IProps) { |
||||||
|
return ( |
||||||
|
<ListItem> |
||||||
|
<ListItemIcon> |
||||||
|
<MusicNoteIcon /> |
||||||
|
</ListItemIcon> |
||||||
|
<CircularProgress size={24}/> |
||||||
|
</ListItem> |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
import React, { useEffect } from 'react'; |
||||||
|
import ItemListItem from './ItemListItem'; |
||||||
|
import { SongDisplayItem, LoadingSongDisplayItem } from '../types/DisplayItem'; |
||||||
|
|
||||||
|
export interface IProps { |
||||||
|
getDetails: () => Promise<SongDisplayItem> |
||||||
|
} |
||||||
|
|
||||||
|
export default function ItemListSongItem(props: IProps) { |
||||||
|
const [ song, setSong ] = React.useState<SongDisplayItem | LoadingSongDisplayItem>({ loadingSong: true }); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
props.getDetails() |
||||||
|
.then((details:SongDisplayItem) => { setSong(details); }); |
||||||
|
}); |
||||||
|
|
||||||
|
return <ItemListItem item={song}/> |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
export interface SongDisplayItem { |
||||||
|
title:String, |
||||||
|
artistNames:String[], |
||||||
|
} |
||||||
|
|
||||||
|
export interface LoadingSongDisplayItem { |
||||||
|
loadingSong: boolean, |
||||||
|
} |
||||||
|
|
||||||
|
export type DisplayItem = SongDisplayItem | LoadingSongDisplayItem; |
||||||
|
|
||||||
|
export function isSong(item: DisplayItem): item is SongDisplayItem { |
||||||
|
return "title" in item; |
||||||
|
} |
||||||
|
|
||||||
|
export function isLoadingSong(item: DisplayItem): item is LoadingSongDisplayItem { |
||||||
|
return "loadingSong" in item; |
||||||
|
} |
Loading…
Reference in new issue