Final touches on filter field (cancelable) and list bullets

This commit is contained in:
James Barnsley
2020-11-09 20:55:13 +13:00
parent 68f1c4e3f1
commit 0fd3075730
14 changed files with 415 additions and 311 deletions

View File

@ -17,8 +17,10 @@ export default class FilterField extends React.Component {
}
componentDidMount() {
const { initialValue: value } = this.props;
window.addEventListener('keyup', this.handleKeyUp, false);
this.setState({ value: this.props.initialValue });
this.setState({ value });
}
componentWillUnmount() {
@ -46,6 +48,15 @@ export default class FilterField extends React.Component {
this.setState({ active: true });
}
deactivate = () => {
const {
handleChange: doHandleChange,
} = this.props;
this.setState({ active: false, value: '' });
doHandleChange('');
}
handleChange = ({ target: { value } }) => {
const {
slim_mode,
@ -72,7 +83,7 @@ export default class FilterField extends React.Component {
const { value, active } = this.state;
return (
<span className={`filter-field ${active ? 'active' : ''}`} onClick={this.activate}>
<span className={`filter-field ${active ? 'active' : ''}`}>
<form onSubmit={this.handleSubmit}>
<input
type="text"
@ -82,7 +93,11 @@ export default class FilterField extends React.Component {
onBlur={this.handleBlur}
onChange={this.handleChange}
/>
<Icon name="search" type="material" />
<Icon
name={active ? 'close' : 'search'}
type="material"
onClick={active ? this.deactivate : null}
/>
</form>
</span>
);

View File

@ -13,12 +13,9 @@ import {
scrollTo,
} from '../util/helpers';
import { I18n } from '../locale';
import { arrayOf } from '../util/arrays';
export default class ListItem extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
const { item, mopidyActions, discogsActions } = this.props;
if (!item) return;
@ -42,148 +39,186 @@ export default class ListItem extends React.Component {
}
}
handleClick(e) {
// make sure we haven't clicked a nested link (ie Artist name)
handleClick = (e) => {
const {
history,
link_prefix = '',
item: {
uri,
},
} = this.props;
if (e.target.tagName.toLowerCase() !== 'a') {
e.preventDefault();
this.props.history.push((this.props.link_prefix ? this.props.link_prefix : '') + encodeURIComponent(this.props.item.uri));
history.push(`${link_prefix}${encodeURIComponent(uri)}`);
scrollTo();
}
}
handleMouseDown(e) {
// make sure we haven't clicked a nested link (ie Artist name)
handleMouseDown = (e) => {
const {
history,
link_prefix = '',
item: {
uri,
},
} = this.props;
if (e.target.tagName.toLowerCase() !== 'a') {
e.preventDefault();
this.props.history.push((this.props.link_prefix ? this.props.link_prefix : '') + encodeURIComponent(this.props.item.uri));
history.push(`${link_prefix}${encodeURIComponent(uri)}`);
scrollTo();
}
}
handleContextMenu(e) {
if (this.props.handleContextMenu) {
handleContextMenu = (e) => {
const {
handleContextMenu,
item,
} = this.props;
if (handleContextMenu) {
e.preventDefault();
this.props.handleContextMenu(e, this.props.item);
handleContextMenu(e, item);
}
}
renderValue(key_string) {
const key = key_string.split('.');
let value = Object.assign(this.props.item);
/**
* TODO
*
* THIS WHOLE BLOCK NEEDS A REVISIT.
* Surely there is a cleaner way to pull values? Perhaps simplify it like our new sortItems?
*/
renderValue = (key) => {
const {
item: {
[key]: value,
...item
} = {},
} = this.props;
for (let i = 0; i < key.length; i++) {
if (value[key[i]] === undefined) {
return null;
} if (typeof (value[key[i]]) === 'string' && value[key[i]].replace(' ', '') == '') {
return null;
}
value = value[key[i]];
if (key === 'tracks') {
const {
tracks_total: total,
tracks: array,
} = item;
if (!total && !array) return null;
return <I18n path="specs.tracks" count={total || array.length} />;
}
if (key === 'artists') {
const {
artists_total: total,
artists_uris: array,
} = item;
if (!total && !array) return null;
return <I18n path="specs.artists" count={total || array.length} />;
}
if (key === 'albums') {
const {
albums_total: total,
albums_uris: array,
} = item;
if (!total && !array) return null;
return <I18n path="specs.albums" count={total || array.length} />;
}
if (key_string === 'tracks_total' || key_string === 'tracks_uris.length') {
return (
<span>
<I18n path="specs.tracks" count={value} />
</span>
);
// All options beyond here don't play well with null/undefined
if (!value) return null;
if (key === 'followers') {
return <I18n path="specs.followers" count={value.toLocaleString()} />;
}
if (key_string === 'followers') {
return (
<span>
<I18n path="specs.followers" count={value.toLocaleString()} />
</span>
);
if (key === 'added_at') {
return <I18n path="specs.added_ago" time={dater('ago', value)} />;
}
if (key_string === 'added_at') {
return (
<span>
<I18n path="specs.added_ago" time={dater('ago', value)} />
</span>
);
if (key === 'last_modified') {
return <I18n path="specs.updated_ago" time={dater('ago', value)} />;
}
if (key_string === 'last_modified') {
return (
<span>
<I18n path="specs.updated_ago" time={dater('ago', value)} />
</span>
);
if (key === 'owner') {
return <URILink type="user" uri={value.uri}>{value.id}</URILink>;
}
if (key_string === 'owner') return <URILink type="user" uri={value.uri}>{value.id}</URILink>;
if (key_string === 'popularity') return <Popularity full popularity={value} />;
if (key_string === 'artists') return <LinksSentence items={value} />;
if (key === 'popularity') {
return <Popularity full popularity={value} />;
}
if (value === true) return <Icon name="check" />;
if (typeof (value) === 'number') return <span>{value.toLocaleString()}</span>;
return <span>{value}</span>;
return value;
}
render() {
const { item } = this.props;
const {
item,
middle_column,
right_column,
thumbnail,
details,
nocontext,
} = this.props;
if (!item) {
return null;
}
let class_name = 'list__item';
if (item.type) {
class_name += ` list__item--${item.type}`;
}
if (this.props.middle_column) {
class_name += ' list__item--has-middle-column';
}
if (this.props.thumbnail) {
class_name += ' list__item--has-thumbnail';
}
if (this.props.details) {
class_name += ' list__item--has-details';
}
if (item.type) class_name += ` list__item--${item.type}`;
if (middle_column) class_name += ' list__item--has-middle-column';
if (thumbnail) class_name += ' list__item--has-thumbnail';
if (details) class_name += ' list__item--has-details';
return (
<div
className={class_name}
onClick={(e) => this.handleClick(e)}
onContextMenu={(e) => this.handleContextMenu(e)}
onClick={this.handleClick}
onContextMenu={this.handleContextMenu}
>
{this.props.right_column && !this.props.nocontext
&& (
<div className="list__item__column list__item__column--right">
{
(this.props.right_column ? this.props.right_column.map((column, index) => (
<span className={`list__item__column__item list__item__column__item--${column.replace('.', '_')}`} key={index}>
{this.renderValue(column, item)}
</span>
)) : null)
{
right_column && !nocontext && (
<div className="list__item__column list__item__column--right">
{
right_column.map((column, index) => (
<span className={`list__item__column__item list__item__column__item--${column.replace('.', '_')}`} key={index}>
{this.renderValue(column, item)}
</span>
))
}
{this.props.nocontext ? null : <ContextMenuTrigger className="list__item__column__item list__item__column__item--context-menu-trigger subtle" onTrigger={(e) => this.handleContextMenu(e)} />}
</div>
)}
{!nocontext && (
<ContextMenuTrigger
className="list__item__column__item list__item__column__item--context-menu-trigger subtle"
onTrigger={this.handleContextMenu}
/>
)}
</div>
)
}
<div className="list__item__column list__item__column--name">
{this.props.thumbnail ? <Thumbnail className="list__item__column__item list__item__column__item--thumbnail" images={(item.images ? item.images : null)} size="small" /> : null}
{thumbnail && (
<Thumbnail
className="list__item__column__item list__item__column__item--thumbnail"
images={item.images}
size="small"
/>
)}
<div className="list__item__column__item list__item__column__item--name">
{item.name !== undefined ? this.renderValue('name') : <span className="grey-text">{item.uri}</span>}
{
item.name !== undefined
? this.renderValue('name')
: <span className="grey-text">{item.uri}</span>
}
</div>
{this.props.details ? (
{details ? (
<ul className="list__item__column__item list__item__column__item--details details">
{
this.props.details.map((detail, index) => {
details.map((detail, index) => {
const value = this.renderValue(detail);
if (!value) {
return null;
}
if (!value) return null;
return (
<li className={`details__item details__item--${detail.replace('.', '_')}`} key={index}>
{value}
</li>
<li
className={`details__item details__item--${detail.replace('.', '_')}`}
key={index}
>
{value}
</li>
);
})
}
@ -191,17 +226,20 @@ export default class ListItem extends React.Component {
) : null}
</div>
{this.props.middle_column ? (
{middle_column && (
<div className="list__item__column list__item__column--middle">
{
(this.props.middle_column ? this.props.middle_column.map((column, index) => (
<span className={`list__item__column__item list__item__column__item--${column.replace('.', '_')}`} key={index}>
{this.renderValue(column)}
</span>
)) : null)
middle_column.map((column, index) => (
<span
className={`list__item__column__item list__item__column__item--${column.replace('.', '_')}`}
key={index}
>
{this.renderValue(column)}
</span>
))
}
</div>
) : null}
)}
</div>
);
}

View File

@ -218,7 +218,7 @@ class LibraryAlbums extends React.Component {
handleContextMenu={this.handleContextMenu}
rows={albums}
thumbnail
details={['artists', 'tracks_total', 'added_at']}
details={['artists', 'tracks', 'added_at']}
right_column={['added_at']}
className="albums"
link_prefix="/album/"

View File

@ -194,7 +194,7 @@ class LibraryArtists extends React.Component {
handleContextMenu={(e, item) => this.handleContextMenu(e, item)}
rows={artists}
thumbnail
details={['followers']}
details={['albums', 'followers']}
middle_column={['source']}
className="artists"
link_prefix="/artist/"

View File

@ -184,7 +184,7 @@ class LibraryPlaylists extends React.Component {
handleContextMenu={(e, item) => this.handleContextMenu(e, item)}
rows={playlists}
thumbnail
details={['owner', 'tracks_total', 'last_modified']}
details={['owner', 'tracks', 'last_modified']}
right_column={['source']}
className="playlists"
link_prefix="/playlist/"

View File

@ -9,9 +9,9 @@
.icon {
position: absolute;
top: 12px;
left: 15px;
pointer-events: none;
padding: 12px;
top: 0;
left: 0;
}
input {
@ -26,6 +26,10 @@
cursor: pointer;
background: transparent !important;
}
.icon {
pointer-events: none;
}
&:hover {
.icon {
@ -39,6 +43,13 @@
width: 150px;
padding-left: 35px;
}
.icon {
&:hover {
color: colour(red);
cursor: pointer;
}
}
}
}

View File

@ -192,6 +192,7 @@
&__name {
padding-top: 0;
margin-bottom: 5px;
display: block;
}
.source {