mirror of https://github.com/lidarr/Lidarr
[UI Work] Add Overview Artist Index View
This commit is contained in:
parent
f6fc78d927
commit
566ac1a9d3
|
@ -1,4 +1,4 @@
|
||||||
import _ from 'lodash';
|
/* eslint max-params: 0 */
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||||
|
|
|
@ -163,6 +163,7 @@ class ArtistDetails extends Component {
|
||||||
foreignArtistId,
|
foreignArtistId,
|
||||||
artistName,
|
artistName,
|
||||||
ratings,
|
ratings,
|
||||||
|
path,
|
||||||
sizeOnDisk,
|
sizeOnDisk,
|
||||||
trackFileCount,
|
trackFileCount,
|
||||||
qualityProfileId,
|
qualityProfileId,
|
||||||
|
@ -343,6 +344,21 @@ class ArtistDetails extends Component {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.detailsLabels}>
|
<div className={styles.detailsLabels}>
|
||||||
|
<Label
|
||||||
|
className={styles.detailsLabel}
|
||||||
|
title={trackFilesCountMessage}
|
||||||
|
size={sizes.LARGE}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name={icons.FOLDER}
|
||||||
|
size={17}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span className={styles.sizeOnDisk}>
|
||||||
|
{path}
|
||||||
|
</span>
|
||||||
|
</Label>
|
||||||
|
|
||||||
<Label
|
<Label
|
||||||
className={styles.detailsLabel}
|
className={styles.detailsLabel}
|
||||||
title={trackFilesCountMessage}
|
title={trackFilesCountMessage}
|
||||||
|
@ -547,6 +563,7 @@ ArtistDetails.propTypes = {
|
||||||
foreignArtistId: PropTypes.string.isRequired,
|
foreignArtistId: PropTypes.string.isRequired,
|
||||||
artistName: PropTypes.string.isRequired,
|
artistName: PropTypes.string.isRequired,
|
||||||
ratings: PropTypes.object.isRequired,
|
ratings: PropTypes.object.isRequired,
|
||||||
|
path: PropTypes.string.isRequired,
|
||||||
sizeOnDisk: PropTypes.number.isRequired,
|
sizeOnDisk: PropTypes.number.isRequired,
|
||||||
trackFileCount: PropTypes.number,
|
trackFileCount: PropTypes.number,
|
||||||
qualityProfileId: PropTypes.number.isRequired,
|
qualityProfileId: PropTypes.number.isRequired,
|
||||||
|
|
|
@ -17,6 +17,8 @@ import ArtistIndexPosterOptionsModal from './Posters/Options/ArtistIndexPosterOp
|
||||||
import ArtistIndexPostersConnector from './Posters/ArtistIndexPostersConnector';
|
import ArtistIndexPostersConnector from './Posters/ArtistIndexPostersConnector';
|
||||||
import ArtistIndexBannerOptionsModal from './Banners/Options/ArtistIndexBannerOptionsModal';
|
import ArtistIndexBannerOptionsModal from './Banners/Options/ArtistIndexBannerOptionsModal';
|
||||||
import ArtistIndexBannersConnector from './Banners/ArtistIndexBannersConnector';
|
import ArtistIndexBannersConnector from './Banners/ArtistIndexBannersConnector';
|
||||||
|
import ArtistIndexOverviewOptionsModal from './Overview/Options/ArtistIndexOverviewOptionsModal';
|
||||||
|
import ArtistIndexOverviewsConnector from './Overview/ArtistIndexOverviewsConnector';
|
||||||
import ArtistIndexFooter from './ArtistIndexFooter';
|
import ArtistIndexFooter from './ArtistIndexFooter';
|
||||||
import ArtistIndexFilterMenu from './Menus/ArtistIndexFilterMenu';
|
import ArtistIndexFilterMenu from './Menus/ArtistIndexFilterMenu';
|
||||||
import ArtistIndexSortMenu from './Menus/ArtistIndexSortMenu';
|
import ArtistIndexSortMenu from './Menus/ArtistIndexSortMenu';
|
||||||
|
@ -32,6 +34,10 @@ function getViewComponent(view) {
|
||||||
return ArtistIndexBannersConnector;
|
return ArtistIndexBannersConnector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (view === 'overview') {
|
||||||
|
return ArtistIndexOverviewsConnector;
|
||||||
|
}
|
||||||
|
|
||||||
return ArtistIndexTableConnector;
|
return ArtistIndexTableConnector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +56,7 @@ class ArtistIndex extends Component {
|
||||||
jumpBarItems: [],
|
jumpBarItems: [],
|
||||||
isPosterOptionsModalOpen: false,
|
isPosterOptionsModalOpen: false,
|
||||||
isBannerOptionsModalOpen: false,
|
isBannerOptionsModalOpen: false,
|
||||||
|
isOverviewOptionsModalOpen: false,
|
||||||
isRendered: false
|
isRendered: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -137,6 +144,14 @@ class ArtistIndex extends Component {
|
||||||
this.setState({ isBannerOptionsModalOpen: false });
|
this.setState({ isBannerOptionsModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onOverviewOptionsPress = () => {
|
||||||
|
this.setState({ isOverviewOptionsModalOpen: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
onOverviewOptionsModalClose = () => {
|
||||||
|
this.setState({ isOverviewOptionsModalOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
onJumpBarItemPress = (item) => {
|
onJumpBarItemPress = (item) => {
|
||||||
const viewComponent = this._viewComponent.getWrappedInstance();
|
const viewComponent = this._viewComponent.getWrappedInstance();
|
||||||
viewComponent.scrollToFirstCharacter(item);
|
viewComponent.scrollToFirstCharacter(item);
|
||||||
|
@ -193,6 +208,7 @@ class ArtistIndex extends Component {
|
||||||
jumpBarItems,
|
jumpBarItems,
|
||||||
isPosterOptionsModalOpen,
|
isPosterOptionsModalOpen,
|
||||||
isBannerOptionsModalOpen,
|
isBannerOptionsModalOpen,
|
||||||
|
isOverviewOptionsModalOpen,
|
||||||
isRendered
|
isRendered
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
|
@ -234,11 +250,6 @@ class ArtistIndex extends Component {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
view === 'posters' &&
|
|
||||||
<PageToolbarSeparator />
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
view === 'banners' &&
|
view === 'banners' &&
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
|
@ -249,7 +260,17 @@ class ArtistIndex extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
view === 'banners' &&
|
view === 'overview' &&
|
||||||
|
<PageToolbarButton
|
||||||
|
label="Options"
|
||||||
|
iconName={icons.OVERVIEW}
|
||||||
|
onPress={this.onOverviewOptionsPress}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
(view === 'posters' || view === 'banners' || view === 'overview') &&
|
||||||
|
|
||||||
<PageToolbarSeparator />
|
<PageToolbarSeparator />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,6 +351,13 @@ class ArtistIndex extends Component {
|
||||||
<ArtistIndexBannerOptionsModal
|
<ArtistIndexBannerOptionsModal
|
||||||
isOpen={isBannerOptionsModalOpen}
|
isOpen={isBannerOptionsModalOpen}
|
||||||
onModalClose={this.onBannerOptionsModalClose}
|
onModalClose={this.onBannerOptionsModalClose}
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ArtistIndexOverviewOptionsModal
|
||||||
|
isOpen={isOverviewOptionsModalOpen}
|
||||||
|
onModalClose={this.onOverviewOptionsModalClose}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Link from 'Components/Link/Link';
|
||||||
import ArtistBanner from 'Artist/ArtistBanner';
|
import ArtistBanner from 'Artist/ArtistBanner';
|
||||||
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
||||||
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
||||||
import ArtistIndexBannerProgressBar from './ArtistIndexBannerProgressBar';
|
import ArtistIndexProgressBar from 'Artist/Index/ProgressBar/ArtistIndexProgressBar';
|
||||||
import ArtistIndexBannerInfo from './ArtistIndexBannerInfo';
|
import ArtistIndexBannerInfo from './ArtistIndexBannerInfo';
|
||||||
import styles from './ArtistIndexBanner.css';
|
import styles from './ArtistIndexBanner.css';
|
||||||
|
|
||||||
|
@ -135,12 +135,12 @@ class ArtistIndexBanner extends Component {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ArtistIndexBannerProgressBar
|
<ArtistIndexProgressBar
|
||||||
monitored={monitored}
|
monitored={monitored}
|
||||||
status={status}
|
status={status}
|
||||||
trackCount={trackCount}
|
trackCount={trackCount}
|
||||||
trackFileCount={trackFileCount}
|
trackFileCount={trackFileCount}
|
||||||
bannerWidth={bannerWidth}
|
posterWidth={bannerWidth}
|
||||||
detailedProgressBar={detailedProgressBar}
|
detailedProgressBar={detailedProgressBar}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -174,6 +174,7 @@ class ArtistIndexBanner extends Component {
|
||||||
|
|
||||||
<ArtistIndexBannerInfo
|
<ArtistIndexBannerInfo
|
||||||
qualityProfile={qualityProfile}
|
qualityProfile={qualityProfile}
|
||||||
|
showQualityProfile={showQualityProfile}
|
||||||
showRelativeDates={showRelativeDates}
|
showRelativeDates={showRelativeDates}
|
||||||
shortDateFormat={shortDateFormat}
|
shortDateFormat={shortDateFormat}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import styles from './ArtistIndexBannerInfo.css';
|
||||||
function ArtistIndexBannerInfo(props) {
|
function ArtistIndexBannerInfo(props) {
|
||||||
const {
|
const {
|
||||||
qualityProfile,
|
qualityProfile,
|
||||||
|
showQualityProfile,
|
||||||
previousAiring,
|
previousAiring,
|
||||||
added,
|
added,
|
||||||
albumCount,
|
albumCount,
|
||||||
|
@ -18,7 +19,7 @@ function ArtistIndexBannerInfo(props) {
|
||||||
timeFormat
|
timeFormat
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
if (sortKey === 'qualityProfileId') {
|
if (sortKey === 'qualityProfileId' && !showQualityProfile) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>
|
||||||
{qualityProfile.name}
|
{qualityProfile.name}
|
||||||
|
@ -99,6 +100,7 @@ function ArtistIndexBannerInfo(props) {
|
||||||
|
|
||||||
ArtistIndexBannerInfo.propTypes = {
|
ArtistIndexBannerInfo.propTypes = {
|
||||||
qualityProfile: PropTypes.object.isRequired,
|
qualityProfile: PropTypes.object.isRequired,
|
||||||
|
showQualityProfile: PropTypes.bool.isRequired,
|
||||||
previousAiring: PropTypes.string,
|
previousAiring: PropTypes.string,
|
||||||
added: PropTypes.string,
|
added: PropTypes.string,
|
||||||
albumCount: PropTypes.number.isRequired,
|
albumCount: PropTypes.number.isRequired,
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import getProgressBarKind from 'Utilities/Series/getProgressBarKind';
|
|
||||||
import { sizes } from 'Helpers/Props';
|
|
||||||
import ProgressBar from 'Components/ProgressBar';
|
|
||||||
import styles from './ArtistIndexBannerProgressBar.css';
|
|
||||||
|
|
||||||
function ArtistIndexBannerProgressBar(props) {
|
|
||||||
const {
|
|
||||||
monitored,
|
|
||||||
status,
|
|
||||||
trackCount,
|
|
||||||
trackFileCount,
|
|
||||||
bannerWidth,
|
|
||||||
detailedProgressBar
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const progress = trackCount ? trackFileCount / trackCount * 100 : 100;
|
|
||||||
const text = `${trackFileCount} / ${trackCount}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ProgressBar
|
|
||||||
className={styles.progressBar}
|
|
||||||
containerClassName={styles.progress}
|
|
||||||
progress={progress}
|
|
||||||
kind={getProgressBarKind(status, monitored, progress)}
|
|
||||||
size={detailedProgressBar ? sizes.MEDIUM : sizes.SMALL}
|
|
||||||
showText={detailedProgressBar}
|
|
||||||
text={text}
|
|
||||||
title={detailedProgressBar ? null : text}
|
|
||||||
width={bannerWidth}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArtistIndexBannerProgressBar.propTypes = {
|
|
||||||
monitored: PropTypes.bool.isRequired,
|
|
||||||
status: PropTypes.string.isRequired,
|
|
||||||
trackCount: PropTypes.number.isRequired,
|
|
||||||
trackFileCount: PropTypes.number.isRequired,
|
|
||||||
bannerWidth: PropTypes.number.isRequired,
|
|
||||||
detailedProgressBar: PropTypes.bool.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ArtistIndexBannerProgressBar;
|
|
|
@ -12,8 +12,8 @@ import ArtistIndexBanner from './ArtistIndexBanner';
|
||||||
import styles from './ArtistIndexBanners.css';
|
import styles from './ArtistIndexBanners.css';
|
||||||
|
|
||||||
// container dimensions
|
// container dimensions
|
||||||
const columnPadding = 20;
|
const columnPadding = parseInt(dimensions.artistIndexColumnPadding);
|
||||||
const columnPaddingSmallScreen = 10;
|
const columnPaddingSmallScreen = parseInt(dimensions.artistIndexColumnPaddingSmallScreen);
|
||||||
const progressBarHeight = parseInt(dimensions.progressBarSmallHeight);
|
const progressBarHeight = parseInt(dimensions.progressBarSmallHeight);
|
||||||
const detailedProgressBarHeight = parseInt(dimensions.progressBarMediumHeight);
|
const detailedProgressBarHeight = parseInt(dimensions.progressBarMediumHeight);
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ class ArtistIndexBanners extends Component {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const padding = isSmallScreen ? columnPaddingSmallScreen : columnPadding;
|
const padding = isSmallScreen ? columnPaddingSmallScreen : columnPadding;
|
||||||
const columnWidth = calculateColumnWidth(width, this.props.bannerOptions.size);
|
const columnWidth = calculateColumnWidth(width, bannerOptions.size, isSmallScreen);
|
||||||
const columnCount = Math.max(Math.floor(width / columnWidth), 1);
|
const columnCount = Math.max(Math.floor(width / columnWidth), 1);
|
||||||
const bannerWidth = columnWidth - padding;
|
const bannerWidth = columnWidth - padding;
|
||||||
const bannerHeight = calculateHeight(bannerWidth);
|
const bannerHeight = calculateHeight(bannerWidth);
|
||||||
|
@ -300,7 +300,7 @@ class ArtistIndexBanners extends Component {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</WindowScroller>
|
</WindowScroller>
|
||||||
</Measure>
|
</Measure>
|
||||||
);
|
);
|
||||||
|
|
|
@ -37,6 +37,14 @@ function ArtistIndexViewMenu(props) {
|
||||||
>
|
>
|
||||||
Banners
|
Banners
|
||||||
</ViewMenuItem>
|
</ViewMenuItem>
|
||||||
|
|
||||||
|
<ViewMenuItem
|
||||||
|
name="overview"
|
||||||
|
selectedView={view}
|
||||||
|
onPress={onViewSelect}
|
||||||
|
>
|
||||||
|
Overview
|
||||||
|
</ViewMenuItem>
|
||||||
</MenuContent>
|
</MenuContent>
|
||||||
</ViewMenu>
|
</ViewMenu>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
$hoverScale: 1.05;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.posterContainer {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
composes: link from 'Components/Link/Link.css';
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
color: $defaultColor;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $defaultColor;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ended {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-width: 0 25px 25px 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent $dangerColor transparent transparent;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
flex: 1 0 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleRow {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@add-mixin truncate;
|
||||||
|
composes: link;
|
||||||
|
|
||||||
|
flex: 1 0 1px;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview {
|
||||||
|
composes: link;
|
||||||
|
|
||||||
|
flex: 0 1 1000px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: $breakpointSmall) {
|
||||||
|
.overview {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,250 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import Truncate from 'react-truncate';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import dimensions from 'Styles/Variables/dimensions';
|
||||||
|
import fonts from 'Styles/Variables/fonts';
|
||||||
|
import IconButton from 'Components/Link/IconButton';
|
||||||
|
import Link from 'Components/Link/Link';
|
||||||
|
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||||
|
import ArtistPoster from 'Artist/ArtistPoster';
|
||||||
|
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
||||||
|
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
||||||
|
import ArtistIndexProgressBar from 'Artist/Index/ProgressBar/ArtistIndexProgressBar';
|
||||||
|
import ArtistIndexOverviewInfo from './ArtistIndexOverviewInfo';
|
||||||
|
import styles from './ArtistIndexOverview.css';
|
||||||
|
|
||||||
|
const columnPadding = parseInt(dimensions.artistIndexColumnPadding);
|
||||||
|
const columnPaddingSmallScreen = parseInt(dimensions.artistIndexColumnPaddingSmallScreen);
|
||||||
|
const defaultFontSize = parseInt(fonts.defaultFontSize);
|
||||||
|
const lineHeight = parseFloat(fonts.lineHeight);
|
||||||
|
|
||||||
|
function calculateHeight(rowHeight, isSmallScreen) {
|
||||||
|
let height = rowHeight - 45;
|
||||||
|
|
||||||
|
if (isSmallScreen) {
|
||||||
|
height -= columnPaddingSmallScreen;
|
||||||
|
} else {
|
||||||
|
height -= columnPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArtistIndexOverview extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isEditArtistModalOpen: false,
|
||||||
|
isDeleteArtistModalOpen: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onEditArtistPress = () => {
|
||||||
|
this.setState({ isEditArtistModalOpen: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditArtistModalClose = () => {
|
||||||
|
this.setState({ isEditArtistModalOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteArtistPress = () => {
|
||||||
|
this.setState({
|
||||||
|
isEditArtistModalOpen: false,
|
||||||
|
isDeleteArtistModalOpen: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteArtistModalClose = () => {
|
||||||
|
this.setState({ isDeleteArtistModalOpen: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
style,
|
||||||
|
id,
|
||||||
|
artistName,
|
||||||
|
overview,
|
||||||
|
monitored,
|
||||||
|
status,
|
||||||
|
nameSlug,
|
||||||
|
nextAiring,
|
||||||
|
trackCount,
|
||||||
|
trackFileCount,
|
||||||
|
images,
|
||||||
|
posterWidth,
|
||||||
|
posterHeight,
|
||||||
|
qualityProfile,
|
||||||
|
overviewOptions,
|
||||||
|
showRelativeDates,
|
||||||
|
shortDateFormat,
|
||||||
|
timeFormat,
|
||||||
|
rowHeight,
|
||||||
|
isSmallScreen,
|
||||||
|
isRefreshingArtist,
|
||||||
|
onRefreshArtistPress,
|
||||||
|
...otherProps
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
isEditArtistModalOpen,
|
||||||
|
isDeleteArtistModalOpen
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
const link = `/artist/${nameSlug}`;
|
||||||
|
|
||||||
|
const elementStyle = {
|
||||||
|
width: `${posterWidth}px`,
|
||||||
|
height: `${posterHeight}px`
|
||||||
|
};
|
||||||
|
|
||||||
|
const height = calculateHeight(rowHeight, isSmallScreen);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container} style={style}>
|
||||||
|
<div className={styles.poster} style={elementStyle}>
|
||||||
|
<div className={styles.posterContainer}>
|
||||||
|
{
|
||||||
|
status === 'ended' &&
|
||||||
|
<div
|
||||||
|
className={styles.ended}
|
||||||
|
title="Ended"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
<Link
|
||||||
|
className={styles.link}
|
||||||
|
style={elementStyle}
|
||||||
|
to={link}
|
||||||
|
>
|
||||||
|
<ArtistPoster
|
||||||
|
className={styles.poster}
|
||||||
|
style={elementStyle}
|
||||||
|
images={images}
|
||||||
|
size={250}
|
||||||
|
lazy={false}
|
||||||
|
overflow={true}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ArtistIndexProgressBar
|
||||||
|
monitored={monitored}
|
||||||
|
status={status}
|
||||||
|
trackCount={trackCount}
|
||||||
|
trackFileCount={trackFileCount}
|
||||||
|
posterWidth={posterWidth}
|
||||||
|
detailedProgressBar={overviewOptions.detailedProgressBar}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.info}>
|
||||||
|
<div className={styles.titleRow}>
|
||||||
|
<Link
|
||||||
|
className={styles.title}
|
||||||
|
to={link}
|
||||||
|
>
|
||||||
|
{artistName}
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className={styles.actions}>
|
||||||
|
<SpinnerIconButton
|
||||||
|
name={icons.REFRESH}
|
||||||
|
title="Refresh artist"
|
||||||
|
isSpinning={isRefreshingArtist}
|
||||||
|
onPress={onRefreshArtistPress}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
name={icons.EDIT}
|
||||||
|
title="Edit Artist"
|
||||||
|
onPress={this.onEditArtistPress}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.details}>
|
||||||
|
<Link
|
||||||
|
className={styles.overview}
|
||||||
|
style={{
|
||||||
|
maxHeight: `${height}px`
|
||||||
|
}}
|
||||||
|
to={link}
|
||||||
|
>
|
||||||
|
<Truncate lines={Math.floor(height / (defaultFontSize * lineHeight))}>
|
||||||
|
{overview}
|
||||||
|
</Truncate>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<ArtistIndexOverviewInfo
|
||||||
|
height={height}
|
||||||
|
nextAiring={nextAiring}
|
||||||
|
qualityProfile={qualityProfile}
|
||||||
|
showRelativeDates={showRelativeDates}
|
||||||
|
shortDateFormat={shortDateFormat}
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
{...overviewOptions}
|
||||||
|
{...otherProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<EditArtistModalConnector
|
||||||
|
isOpen={isEditArtistModalOpen}
|
||||||
|
artistId={id}
|
||||||
|
onModalClose={this.onEditArtistModalClose}
|
||||||
|
onDeleteArtistPress={this.onDeleteArtistPress}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DeleteArtistModal
|
||||||
|
isOpen={isDeleteArtistModalOpen}
|
||||||
|
artistId={id}
|
||||||
|
onModalClose={this.onDeleteArtistModalClose}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistIndexOverview.propTypes = {
|
||||||
|
style: PropTypes.object.isRequired,
|
||||||
|
id: PropTypes.number.isRequired,
|
||||||
|
artistName: PropTypes.string.isRequired,
|
||||||
|
overview: PropTypes.string.isRequired,
|
||||||
|
monitored: PropTypes.bool.isRequired,
|
||||||
|
status: PropTypes.string.isRequired,
|
||||||
|
nameSlug: PropTypes.string.isRequired,
|
||||||
|
nextAiring: PropTypes.string,
|
||||||
|
trackCount: PropTypes.number,
|
||||||
|
trackFileCount: PropTypes.number,
|
||||||
|
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
posterWidth: PropTypes.number.isRequired,
|
||||||
|
posterHeight: PropTypes.number.isRequired,
|
||||||
|
rowHeight: PropTypes.number.isRequired,
|
||||||
|
qualityProfile: PropTypes.object.isRequired,
|
||||||
|
overviewOptions: PropTypes.object.isRequired,
|
||||||
|
showRelativeDates: PropTypes.bool.isRequired,
|
||||||
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
|
timeFormat: PropTypes.string.isRequired,
|
||||||
|
isSmallScreen: PropTypes.bool.isRequired,
|
||||||
|
isRefreshingArtist: PropTypes.bool.isRequired,
|
||||||
|
onRefreshArtistPress: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
ArtistIndexOverview.defaultProps = {
|
||||||
|
trackCount: 0,
|
||||||
|
trackFileCount: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArtistIndexOverview;
|
|
@ -0,0 +1,23 @@
|
||||||
|
.infos {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 250px;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
flex: 0 0 $artistIndexOverviewInfoRowHeight;
|
||||||
|
margin: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
width: 25px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: $breakpointSmall) {
|
||||||
|
.infos {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import getRelativeDate from 'Utilities/Date/getRelativeDate';
|
||||||
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import dimensions from 'Styles/Variables/dimensions';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import styles from './ArtistIndexOverviewInfo.css';
|
||||||
|
|
||||||
|
const infoRowHeight = parseInt(dimensions.artistIndexOverviewInfoRowHeight);
|
||||||
|
|
||||||
|
function isVisible(name, show, value, sortKey, index) {
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return show || sortKey === name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ArtistIndexOverviewInfo(props) {
|
||||||
|
const {
|
||||||
|
height,
|
||||||
|
showQualityProfile,
|
||||||
|
showAdded,
|
||||||
|
showAlbumCount,
|
||||||
|
showPath,
|
||||||
|
showSizeOnDisk,
|
||||||
|
nextAiring,
|
||||||
|
qualityProfile,
|
||||||
|
added,
|
||||||
|
albumCount,
|
||||||
|
path,
|
||||||
|
sizeOnDisk,
|
||||||
|
sortKey,
|
||||||
|
showRelativeDates,
|
||||||
|
shortDateFormat,
|
||||||
|
timeFormat
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
let albums = '1 album';
|
||||||
|
|
||||||
|
if (albumCount === 0) {
|
||||||
|
albums = 'No albums';
|
||||||
|
} else if (albumCount > 1) {
|
||||||
|
albums = `${albumCount} albums`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxRows = Math.floor(height / (infoRowHeight + 4));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.infos}>
|
||||||
|
{
|
||||||
|
!!nextAiring &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Next Airing"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.SCHEDULED}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
getRelativeDate(
|
||||||
|
nextAiring,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates,
|
||||||
|
{
|
||||||
|
timeFormat,
|
||||||
|
timeForToday: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isVisible('qualityProfileId', showQualityProfile, qualityProfile, sortKey) && maxRows > 1 &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Quality Profile"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.PROFILE}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{qualityProfile.name}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isVisible('added', showAdded, added, sortKey) && maxRows > 2 &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Date Added"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.ADD}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{
|
||||||
|
getRelativeDate(
|
||||||
|
added,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates,
|
||||||
|
{
|
||||||
|
timeFormat,
|
||||||
|
timeForToday: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isVisible('albumCount', showAlbumCount, albumCount, sortKey) && maxRows > 3 &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Album Count"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.CIRCLE}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{albums}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isVisible('path', showPath, path, sortKey) && maxRows > 4 &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Path"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.FOLDER}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{path}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isVisible('sizeOnDisk', showSizeOnDisk, sizeOnDisk, sortKey) && maxRows > 5 &&
|
||||||
|
<div
|
||||||
|
className={styles.info}
|
||||||
|
title="Size on Disk"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className={styles.icon}
|
||||||
|
name={icons.DRIVE}
|
||||||
|
size={14}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{formatBytes(sizeOnDisk)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistIndexOverviewInfo.propTypes = {
|
||||||
|
height: PropTypes.number.isRequired,
|
||||||
|
showNetwork: PropTypes.bool.isRequired,
|
||||||
|
showQualityProfile: PropTypes.bool.isRequired,
|
||||||
|
showAdded: PropTypes.bool.isRequired,
|
||||||
|
showAlbumCount: PropTypes.bool.isRequired,
|
||||||
|
showPath: PropTypes.bool.isRequired,
|
||||||
|
showSizeOnDisk: PropTypes.bool.isRequired,
|
||||||
|
nextAiring: PropTypes.string,
|
||||||
|
qualityProfile: PropTypes.object.isRequired,
|
||||||
|
previousAiring: PropTypes.string,
|
||||||
|
added: PropTypes.string,
|
||||||
|
albumCount: PropTypes.number.isRequired,
|
||||||
|
path: PropTypes.string.isRequired,
|
||||||
|
sizeOnDisk: PropTypes.number,
|
||||||
|
sortKey: PropTypes.string.isRequired,
|
||||||
|
showRelativeDates: PropTypes.bool.isRequired,
|
||||||
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
|
timeFormat: PropTypes.string.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArtistIndexOverviewInfo;
|
|
@ -0,0 +1,3 @@
|
||||||
|
.grid {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
|
@ -0,0 +1,278 @@
|
||||||
|
import _ from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import Measure from 'react-measure';
|
||||||
|
import { Grid, WindowScroller } from 'react-virtualized';
|
||||||
|
import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
|
||||||
|
import dimensions from 'Styles/Variables/dimensions';
|
||||||
|
import { sortDirections } from 'Helpers/Props';
|
||||||
|
import ArtistIndexItemConnector from 'Artist/Index/ArtistIndexItemConnector';
|
||||||
|
import ArtistIndexOverview from './ArtistIndexOverview';
|
||||||
|
import styles from './ArtistIndexOverviews.css';
|
||||||
|
|
||||||
|
// Poster container dimensions
|
||||||
|
const columnPadding = parseInt(dimensions.artistIndexColumnPadding);
|
||||||
|
const columnPaddingSmallScreen = parseInt(dimensions.artistIndexColumnPaddingSmallScreen);
|
||||||
|
const progressBarHeight = parseInt(dimensions.progressBarSmallHeight);
|
||||||
|
const detailedProgressBarHeight = parseInt(dimensions.progressBarMediumHeight);
|
||||||
|
|
||||||
|
function calculatePosterWidth(posterSize, isSmallScreen) {
|
||||||
|
const maxiumPosterWidth = isSmallScreen ? 192 : 202;
|
||||||
|
|
||||||
|
if (posterSize === 'large') {
|
||||||
|
return maxiumPosterWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (posterSize === 'medium') {
|
||||||
|
return Math.floor(maxiumPosterWidth * 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.floor(maxiumPosterWidth * 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculateRowHeight(posterHeight, sortKey, isSmallScreen, overviewOptions) {
|
||||||
|
const {
|
||||||
|
detailedProgressBar
|
||||||
|
} = overviewOptions;
|
||||||
|
|
||||||
|
const heights = [
|
||||||
|
posterHeight,
|
||||||
|
detailedProgressBar ? detailedProgressBarHeight : progressBarHeight,
|
||||||
|
isSmallScreen ? columnPaddingSmallScreen : columnPadding
|
||||||
|
];
|
||||||
|
|
||||||
|
return heights.reduce((acc, height) => acc + height, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function calculatePosterHeight(posterWidth) {
|
||||||
|
return posterWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArtistIndexOverviews extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
width: 0,
|
||||||
|
columnCount: 1,
|
||||||
|
posterWidth: 238,
|
||||||
|
posterHeight: 238,
|
||||||
|
rowHeight: calculateRowHeight(238, null, props.isSmallScreen, {})
|
||||||
|
};
|
||||||
|
|
||||||
|
this._isInitialized = false;
|
||||||
|
this._grid = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._contentBodyNode = ReactDOM.findDOMNode(this.props.contentBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
const {
|
||||||
|
items,
|
||||||
|
filterKey,
|
||||||
|
filterValue,
|
||||||
|
sortKey,
|
||||||
|
sortDirection,
|
||||||
|
overviewOptions
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const itemsChanged = hasDifferentItems(prevProps.items, items);
|
||||||
|
const overviewOptionsChanged = !_.isMatch(prevProps.overviewOptions, overviewOptions);
|
||||||
|
|
||||||
|
if (
|
||||||
|
prevProps.sortKey !== sortKey ||
|
||||||
|
prevProps.overviewOptions !== overviewOptions ||
|
||||||
|
itemsChanged
|
||||||
|
) {
|
||||||
|
this.calculateGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
prevProps.filterKey !== filterKey ||
|
||||||
|
prevProps.filterValue !== filterValue ||
|
||||||
|
prevProps.sortKey !== sortKey ||
|
||||||
|
prevProps.sortDirection !== sortDirection ||
|
||||||
|
itemsChanged ||
|
||||||
|
overviewOptionsChanged
|
||||||
|
) {
|
||||||
|
this._grid.recomputeGridSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control
|
||||||
|
|
||||||
|
scrollToFirstCharacter(character) {
|
||||||
|
const items = this.props.items;
|
||||||
|
const {
|
||||||
|
rowHeight
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
const index = _.findIndex(items, (item) => {
|
||||||
|
const firstCharacter = item.sortTitle.charAt(0);
|
||||||
|
|
||||||
|
if (character === '#') {
|
||||||
|
return !isNaN(firstCharacter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstCharacter === character;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (index != null) {
|
||||||
|
const scrollTop = rowHeight * index;
|
||||||
|
|
||||||
|
this.props.onScroll({ scrollTop });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setGridRef = (ref) => {
|
||||||
|
this._grid = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateGrid = (width = this.state.width, isSmallScreen) => {
|
||||||
|
const {
|
||||||
|
sortKey,
|
||||||
|
overviewOptions
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const posterWidth = calculatePosterWidth(overviewOptions.size, isSmallScreen);
|
||||||
|
const posterHeight = calculatePosterHeight(posterWidth);
|
||||||
|
const rowHeight = calculateRowHeight(posterHeight, sortKey, isSmallScreen, overviewOptions);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
width,
|
||||||
|
posterWidth,
|
||||||
|
posterHeight,
|
||||||
|
rowHeight
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cellRenderer = ({ key, rowIndex, style }) => {
|
||||||
|
const {
|
||||||
|
items,
|
||||||
|
sortKey,
|
||||||
|
overviewOptions,
|
||||||
|
showRelativeDates,
|
||||||
|
shortDateFormat,
|
||||||
|
timeFormat,
|
||||||
|
isSmallScreen
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
posterWidth,
|
||||||
|
posterHeight,
|
||||||
|
rowHeight
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
const artist = items[rowIndex];
|
||||||
|
|
||||||
|
if (!artist) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ArtistIndexItemConnector
|
||||||
|
key={key}
|
||||||
|
component={ArtistIndexOverview}
|
||||||
|
sortKey={sortKey}
|
||||||
|
posterWidth={posterWidth}
|
||||||
|
posterHeight={posterHeight}
|
||||||
|
rowHeight={rowHeight}
|
||||||
|
overviewOptions={overviewOptions}
|
||||||
|
showRelativeDates={showRelativeDates}
|
||||||
|
shortDateFormat={shortDateFormat}
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
isSmallScreen={isSmallScreen}
|
||||||
|
style={style}
|
||||||
|
{...artist}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onMeasure = ({ width }) => {
|
||||||
|
this.calculateGrid(width, this.props.isSmallScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSectionRendered = () => {
|
||||||
|
if (!this._isInitialized && this._contentBodyNode) {
|
||||||
|
this.props.onRender();
|
||||||
|
this._isInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
items,
|
||||||
|
scrollTop,
|
||||||
|
isSmallScreen,
|
||||||
|
onScroll
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
width,
|
||||||
|
rowHeight
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Measure onMeasure={this.onMeasure}>
|
||||||
|
<WindowScroller
|
||||||
|
scrollElement={isSmallScreen ? null : this._contentBodyNode}
|
||||||
|
onScroll={onScroll}
|
||||||
|
>
|
||||||
|
{({ height, isScrolling }) => {
|
||||||
|
return (
|
||||||
|
<Grid
|
||||||
|
ref={this.setGridRef}
|
||||||
|
className={styles.grid}
|
||||||
|
autoHeight={true}
|
||||||
|
height={height}
|
||||||
|
columnCount={1}
|
||||||
|
columnWidth={width}
|
||||||
|
rowCount={items.length}
|
||||||
|
rowHeight={rowHeight}
|
||||||
|
width={width}
|
||||||
|
scrollTop={scrollTop}
|
||||||
|
overscanRowCount={2}
|
||||||
|
cellRenderer={this.cellRenderer}
|
||||||
|
onSectionRendered={this.onSectionRendered}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</WindowScroller>
|
||||||
|
</Measure>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistIndexOverviews.propTypes = {
|
||||||
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
filterKey: PropTypes.string,
|
||||||
|
filterValue: PropTypes.oneOfType([PropTypes.bool, PropTypes.number, PropTypes.string]),
|
||||||
|
sortKey: PropTypes.string,
|
||||||
|
sortDirection: PropTypes.oneOf(sortDirections.all),
|
||||||
|
overviewOptions: PropTypes.object.isRequired,
|
||||||
|
scrollTop: PropTypes.number.isRequired,
|
||||||
|
contentBody: PropTypes.object.isRequired,
|
||||||
|
showRelativeDates: PropTypes.bool.isRequired,
|
||||||
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
|
isSmallScreen: PropTypes.bool.isRequired,
|
||||||
|
timeFormat: PropTypes.string.isRequired,
|
||||||
|
onRender: PropTypes.func.isRequired,
|
||||||
|
onScroll: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArtistIndexOverviews;
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import connectSection from 'Store/connectSection';
|
||||||
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
|
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||||
|
import ArtistIndexOverviews from './ArtistIndexOverviews';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state) => state.artistIndex.overviewOptions,
|
||||||
|
createClientSideCollectionSelector(),
|
||||||
|
createUISettingsSelector(),
|
||||||
|
createDimensionsSelector(),
|
||||||
|
(overviewOptions, artist, uiSettings, dimensions) => {
|
||||||
|
return {
|
||||||
|
overviewOptions,
|
||||||
|
showRelativeDates: uiSettings.showRelativeDates,
|
||||||
|
shortDateFormat: uiSettings.shortDateFormat,
|
||||||
|
timeFormat: uiSettings.timeFormat,
|
||||||
|
isSmallScreen: dimensions.isSmallScreen,
|
||||||
|
...artist
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connectSection(
|
||||||
|
createMapStateToProps,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
{ withRef: true },
|
||||||
|
{ section: 'artist', uiSection: 'artistIndex' }
|
||||||
|
)(ArtistIndexOverviews);
|
|
@ -0,0 +1,25 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import ArtistIndexOverviewOptionsModalContentConnector from './ArtistIndexOverviewOptionsModalContentConnector';
|
||||||
|
|
||||||
|
function ArtistIndexOverviewOptionsModal({ isOpen, onModalClose, ...otherProps }) {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
>
|
||||||
|
<ArtistIndexOverviewOptionsModalContentConnector
|
||||||
|
{...otherProps}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistIndexOverviewOptionsModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArtistIndexOverviewOptionsModal;
|
|
@ -0,0 +1,247 @@
|
||||||
|
import _ from 'lodash';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { inputTypes } from 'Helpers/Props';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import Form from 'Components/Form/Form';
|
||||||
|
import FormGroup from 'Components/Form/FormGroup';
|
||||||
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
|
||||||
|
const posterSizeOptions = [
|
||||||
|
{ key: 'small', value: 'Small' },
|
||||||
|
{ key: 'medium', value: 'Medium' },
|
||||||
|
{ key: 'large', value: 'Large' }
|
||||||
|
];
|
||||||
|
|
||||||
|
class ArtistIndexOverviewOptionsModalContent extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
detailedProgressBar: props.detailedProgressBar,
|
||||||
|
size: props.size,
|
||||||
|
showQualityProfile: props.showQualityProfile,
|
||||||
|
showPreviousAiring: props.showPreviousAiring,
|
||||||
|
showAdded: props.showAdded,
|
||||||
|
showAlbumCount: props.showAlbumCount,
|
||||||
|
showPath: props.showPath,
|
||||||
|
showSizeOnDisk: props.showSizeOnDisk
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
const {
|
||||||
|
detailedProgressBar,
|
||||||
|
size,
|
||||||
|
showQualityProfile,
|
||||||
|
showPreviousAiring,
|
||||||
|
showAdded,
|
||||||
|
showAlbumCount,
|
||||||
|
showPath,
|
||||||
|
showSizeOnDisk
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const state = {};
|
||||||
|
|
||||||
|
if (detailedProgressBar !== prevProps.detailedProgressBar) {
|
||||||
|
state.detailedProgressBar = detailedProgressBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size !== prevProps.size) {
|
||||||
|
state.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showQualityProfile !== prevProps.showQualityProfile) {
|
||||||
|
state.showQualityProfile = showQualityProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showPreviousAiring !== prevProps.showPreviousAiring) {
|
||||||
|
state.showPreviousAiring = showPreviousAiring;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showAdded !== prevProps.showAdded) {
|
||||||
|
state.showAdded = showAdded;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showAlbumCount !== prevProps.showAlbumCount) {
|
||||||
|
state.showAlbumCount = showAlbumCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showPath !== prevProps.showPath) {
|
||||||
|
state.showPath = showPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showSizeOnDisk !== prevProps.showSizeOnDisk) {
|
||||||
|
state.showSizeOnDisk = showSizeOnDisk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_.isEmpty(state)) {
|
||||||
|
this.setState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onChangeOverviewOption = ({ name, value }) => {
|
||||||
|
this.setState({
|
||||||
|
[name]: value
|
||||||
|
}, () => {
|
||||||
|
this.props.onChangeOverviewOption({ [name]: value });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
onModalClose
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
detailedProgressBar,
|
||||||
|
size,
|
||||||
|
showQualityProfile,
|
||||||
|
showPreviousAiring,
|
||||||
|
showAdded,
|
||||||
|
showAlbumCount,
|
||||||
|
showPath,
|
||||||
|
showSizeOnDisk
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
Overview Options
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<Form>
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Poster Size</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.SELECT}
|
||||||
|
name="size"
|
||||||
|
value={size}
|
||||||
|
values={posterSizeOptions}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Detailed Progress Bar</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="detailedProgressBar"
|
||||||
|
value={detailedProgressBar}
|
||||||
|
helpText="Show text on progess bar"
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Quality Profile</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showQualityProfile"
|
||||||
|
value={showQualityProfile}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Previous Airing</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showPreviousAiring"
|
||||||
|
value={showPreviousAiring}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Date Added</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showAdded"
|
||||||
|
value={showAdded}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Season Count</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showAlbumCount"
|
||||||
|
value={showAlbumCount}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Path</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showPath"
|
||||||
|
value={showPath}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>Show Size on Disk</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.CHECK}
|
||||||
|
name="showSizeOnDisk"
|
||||||
|
value={showSizeOnDisk}
|
||||||
|
onChange={this.onChangeOverviewOption}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Form>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button
|
||||||
|
onPress={onModalClose}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtistIndexOverviewOptionsModalContent.propTypes = {
|
||||||
|
size: PropTypes.string.isRequired,
|
||||||
|
showQualityProfile: PropTypes.bool.isRequired,
|
||||||
|
showPreviousAiring: PropTypes.bool.isRequired,
|
||||||
|
showAdded: PropTypes.bool.isRequired,
|
||||||
|
showAlbumCount: PropTypes.bool.isRequired,
|
||||||
|
showPath: PropTypes.bool.isRequired,
|
||||||
|
showSizeOnDisk: PropTypes.bool.isRequired,
|
||||||
|
detailedProgressBar: PropTypes.bool.isRequired,
|
||||||
|
onChangeOverviewOption: PropTypes.func.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArtistIndexOverviewOptionsModalContent;
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { setArtistOverviewOption } from 'Store/Actions/artistIndexActions';
|
||||||
|
import ArtistIndexOverviewOptionsModalContent from './ArtistIndexOverviewOptionsModalContent';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state) => state.artistIndex,
|
||||||
|
(artistIndex) => {
|
||||||
|
return artistIndex.overviewOptions;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
|
return {
|
||||||
|
onChangeOverviewOption(payload) {
|
||||||
|
dispatch(setArtistOverviewOption(payload));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, createMapDispatchToProps)(ArtistIndexOverviewOptionsModalContent);
|
|
@ -9,7 +9,7 @@ import Link from 'Components/Link/Link';
|
||||||
import ArtistPoster from 'Artist/ArtistPoster';
|
import ArtistPoster from 'Artist/ArtistPoster';
|
||||||
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
import EditArtistModalConnector from 'Artist/Edit/EditArtistModalConnector';
|
||||||
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
import DeleteArtistModal from 'Artist/Delete/DeleteArtistModal';
|
||||||
import ArtistIndexPosterProgressBar from './ArtistIndexPosterProgressBar';
|
import ArtistIndexProgressBar from 'Artist/Index/ProgressBar/ArtistIndexProgressBar';
|
||||||
import ArtistIndexPosterInfo from './ArtistIndexPosterInfo';
|
import ArtistIndexPosterInfo from './ArtistIndexPosterInfo';
|
||||||
import styles from './ArtistIndexPoster.css';
|
import styles from './ArtistIndexPoster.css';
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ class ArtistIndexPoster extends Component {
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ArtistIndexPosterProgressBar
|
<ArtistIndexProgressBar
|
||||||
monitored={monitored}
|
monitored={monitored}
|
||||||
status={status}
|
status={status}
|
||||||
trackCount={trackCount}
|
trackCount={trackCount}
|
||||||
|
@ -174,6 +174,7 @@ class ArtistIndexPoster extends Component {
|
||||||
|
|
||||||
<ArtistIndexPosterInfo
|
<ArtistIndexPosterInfo
|
||||||
qualityProfile={qualityProfile}
|
qualityProfile={qualityProfile}
|
||||||
|
showQualityProfile={showQualityProfile}
|
||||||
showRelativeDates={showRelativeDates}
|
showRelativeDates={showRelativeDates}
|
||||||
shortDateFormat={shortDateFormat}
|
shortDateFormat={shortDateFormat}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import styles from './ArtistIndexPosterInfo.css';
|
||||||
function ArtistIndexPosterInfo(props) {
|
function ArtistIndexPosterInfo(props) {
|
||||||
const {
|
const {
|
||||||
qualityProfile,
|
qualityProfile,
|
||||||
|
showQualityProfile,
|
||||||
previousAiring,
|
previousAiring,
|
||||||
added,
|
added,
|
||||||
albumCount,
|
albumCount,
|
||||||
|
@ -18,7 +19,7 @@ function ArtistIndexPosterInfo(props) {
|
||||||
timeFormat
|
timeFormat
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
if (sortKey === 'qualityProfileId') {
|
if (sortKey === 'qualityProfileId' && !showQualityProfile) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>
|
||||||
{qualityProfile.name}
|
{qualityProfile.name}
|
||||||
|
@ -99,6 +100,7 @@ function ArtistIndexPosterInfo(props) {
|
||||||
|
|
||||||
ArtistIndexPosterInfo.propTypes = {
|
ArtistIndexPosterInfo.propTypes = {
|
||||||
qualityProfile: PropTypes.object.isRequired,
|
qualityProfile: PropTypes.object.isRequired,
|
||||||
|
showQualityProfile: PropTypes.bool.isRequired,
|
||||||
previousAiring: PropTypes.string,
|
previousAiring: PropTypes.string,
|
||||||
added: PropTypes.string,
|
added: PropTypes.string,
|
||||||
albumCount: PropTypes.number.isRequired,
|
albumCount: PropTypes.number.isRequired,
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
.progress {
|
|
||||||
composes: container from 'Components/ProgressBar.css';
|
|
||||||
|
|
||||||
border-radius: 0;
|
|
||||||
background-color: #5b5b5b;
|
|
||||||
color: $white;
|
|
||||||
transition: width 200ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progressBar {
|
|
||||||
composes: progressBar from 'Components/ProgressBar.css';
|
|
||||||
|
|
||||||
transition: width 200ms ease;
|
|
||||||
}
|
|
|
@ -12,8 +12,8 @@ import ArtistIndexPoster from './ArtistIndexPoster';
|
||||||
import styles from './ArtistIndexPosters.css';
|
import styles from './ArtistIndexPosters.css';
|
||||||
|
|
||||||
// Poster container dimensions
|
// Poster container dimensions
|
||||||
const columnPadding = 20;
|
const columnPadding = parseInt(dimensions.artistIndexColumnPadding);
|
||||||
const columnPaddingSmallScreen = 10;
|
const columnPaddingSmallScreen = parseInt(dimensions.artistIndexColumnPaddingSmallScreen);
|
||||||
const progressBarHeight = parseInt(dimensions.progressBarSmallHeight);
|
const progressBarHeight = parseInt(dimensions.progressBarSmallHeight);
|
||||||
const detailedProgressBarHeight = parseInt(dimensions.progressBarMediumHeight);
|
const detailedProgressBarHeight = parseInt(dimensions.progressBarMediumHeight);
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ class ArtistIndexPosters extends Component {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const padding = isSmallScreen ? columnPaddingSmallScreen : columnPadding;
|
const padding = isSmallScreen ? columnPaddingSmallScreen : columnPadding;
|
||||||
const columnWidth = calculateColumnWidth(width, this.props.posterOptions.size);
|
const columnWidth = calculateColumnWidth(width, posterOptions.size, isSmallScreen);
|
||||||
const columnCount = Math.max(Math.floor(width / columnWidth), 1);
|
const columnCount = Math.max(Math.floor(width / columnWidth), 1);
|
||||||
const posterWidth = columnWidth - padding;
|
const posterWidth = columnWidth - padding;
|
||||||
const posterHeight = calculatePosterHeight(posterWidth);
|
const posterHeight = calculatePosterHeight(posterWidth);
|
||||||
|
|
|
@ -3,9 +3,9 @@ import React from 'react';
|
||||||
import getProgressBarKind from 'Utilities/Series/getProgressBarKind';
|
import getProgressBarKind from 'Utilities/Series/getProgressBarKind';
|
||||||
import { sizes } from 'Helpers/Props';
|
import { sizes } from 'Helpers/Props';
|
||||||
import ProgressBar from 'Components/ProgressBar';
|
import ProgressBar from 'Components/ProgressBar';
|
||||||
import styles from './ArtistIndexPosterProgressBar.css';
|
import styles from './ArtistIndexProgressBar.css';
|
||||||
|
|
||||||
function ArtistIndexPosterProgressBar(props) {
|
function ArtistIndexProgressBar(props) {
|
||||||
const {
|
const {
|
||||||
monitored,
|
monitored,
|
||||||
status,
|
status,
|
||||||
|
@ -33,7 +33,7 @@ function ArtistIndexPosterProgressBar(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArtistIndexPosterProgressBar.propTypes = {
|
ArtistIndexProgressBar.propTypes = {
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
trackCount: PropTypes.number.isRequired,
|
trackCount: PropTypes.number.isRequired,
|
||||||
|
@ -42,4 +42,4 @@ ArtistIndexPosterProgressBar.propTypes = {
|
||||||
detailedProgressBar: PropTypes.bool.isRequired
|
detailedProgressBar: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ArtistIndexPosterProgressBar;
|
export default ArtistIndexProgressBar;
|
|
@ -35,11 +35,10 @@ class CalendarPage extends Component {
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onMeasure = ({ width }) => {
|
onMeasure = ({ width }) => {
|
||||||
this.setState({ width }, () => {
|
this.setState({ width });
|
||||||
const days = Math.max(3, Math.min(7, Math.floor(width / MINIMUM_DAY_WIDTH)));
|
const days = Math.max(3, Math.min(7, Math.floor(width / MINIMUM_DAY_WIDTH)));
|
||||||
console.log(`${width} || ${days}`);
|
|
||||||
this.props.onDaysCountChange(days);
|
this.props.onDaysCountChange(days);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilterMenuItemPress = (filterKey, unmonitored) => {
|
onFilterMenuItemPress = (filterKey, unmonitored) => {
|
||||||
|
@ -109,7 +108,11 @@ class CalendarPage extends Component {
|
||||||
whitelist={['width']}
|
whitelist={['width']}
|
||||||
onMeasure={this.onMeasure}
|
onMeasure={this.onMeasure}
|
||||||
>
|
>
|
||||||
<CalendarConnector />
|
{
|
||||||
|
this.state.width > 0 ?
|
||||||
|
<CalendarConnector /> :
|
||||||
|
<div />
|
||||||
|
}
|
||||||
</Measure>
|
</Measure>
|
||||||
|
|
||||||
<Legend colorImpairedMode={colorImpairedMode} />
|
<Legend colorImpairedMode={colorImpairedMode} />
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.description {
|
.description {
|
||||||
line-height: 1.528571429;
|
line-height: $lineHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.title {
|
.title {
|
||||||
line-height: 1.528571429;
|
line-height: $lineHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
|
|
@ -75,7 +75,6 @@ class PathInput extends Component {
|
||||||
|
|
||||||
onInputBlur = () => {
|
onInputBlur = () => {
|
||||||
this.props.onClearPaths();
|
this.props.onClearPaths();
|
||||||
this.props.onFetchPaths('');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuggestionsFetchRequested = ({ value }) => {
|
onSuggestionsFetchRequested = ({ value }) => {
|
||||||
|
@ -85,7 +84,6 @@ class PathInput extends Component {
|
||||||
onSuggestionsClearRequested = () => {
|
onSuggestionsClearRequested = () => {
|
||||||
// Required because props aren't always rendered, but no-op
|
// Required because props aren't always rendered, but no-op
|
||||||
// because we don't want to reset the paths after a path is selected.
|
// because we don't want to reset the paths after a path is selected.
|
||||||
// `onInputBlur` will handle clearing when the user leaves the input.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuggestionSelected = (event, { suggestionValue }) => {
|
onSuggestionSelected = (event, { suggestionValue }) => {
|
||||||
|
|
|
@ -69,7 +69,7 @@ class SignalRConnector extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
console.log('starting signalR');
|
console.log('Starting signalR');
|
||||||
|
|
||||||
this.signalRconnection = $.connection('/signalr', { apiKey: window.Sonarr.apiKey });
|
this.signalRconnection = $.connection('/signalr', { apiKey: window.Sonarr.apiKey });
|
||||||
|
|
||||||
|
@ -281,7 +281,7 @@ class SignalRConnector extends Component {
|
||||||
|
|
||||||
onStateChanged = (change) => {
|
onStateChanged = (change) => {
|
||||||
const state = getState(change.newState);
|
const state = getState(change.newState);
|
||||||
console.log(`SignalR: [${state}]`);
|
console.log(`SignalR: ${state}`);
|
||||||
|
|
||||||
if (state === 'connected') {
|
if (state === 'connected') {
|
||||||
// Repopulate the page (if a repopulator is set) to ensure things
|
// Repopulate the page (if a repopulator is set) to ensure things
|
||||||
|
|
|
@ -13,6 +13,7 @@ export const CARET_DOWN = 'fa fa-caret-down';
|
||||||
export const CHECK = 'fa fa-check';
|
export const CHECK = 'fa fa-check';
|
||||||
export const CHECK_INDETERMINATE = 'fa fa-minus';
|
export const CHECK_INDETERMINATE = 'fa fa-minus';
|
||||||
export const CHECK_CIRCLE = 'fa fa-check-circle';
|
export const CHECK_CIRCLE = 'fa fa-check-circle';
|
||||||
|
export const CIRCLE = 'fa fa-circle';
|
||||||
export const CIRCLE_OUTLINE = 'fa fa-circle-o';
|
export const CIRCLE_OUTLINE = 'fa fa-circle-o';
|
||||||
export const CLEAR = 'fa fa-trash';
|
export const CLEAR = 'fa fa-trash';
|
||||||
export const CLIPBOARD = 'fa fa-clipboard';
|
export const CLIPBOARD = 'fa fa-clipboard';
|
||||||
|
@ -48,6 +49,7 @@ export const NAVBAR_COLLAPSE = 'fa fa-bars';
|
||||||
export const NOT_AIRED = 'fa fa-clock-o';
|
export const NOT_AIRED = 'fa fa-clock-o';
|
||||||
export const ORGANIZE = 'fa fa-sitemap';
|
export const ORGANIZE = 'fa fa-sitemap';
|
||||||
export const OVERFLOW = 'fa fa-ellipsis-h';
|
export const OVERFLOW = 'fa fa-ellipsis-h';
|
||||||
|
export const OVERVIEW = 'fa fa-th-list';
|
||||||
export const PAGE_FIRST = 'fa fa-fast-backward';
|
export const PAGE_FIRST = 'fa fa-fast-backward';
|
||||||
export const PAGE_PREVIOUS = 'fa fa-backward';
|
export const PAGE_PREVIOUS = 'fa fa-backward';
|
||||||
export const PAGE_NEXT = 'fa fa-forward';
|
export const PAGE_NEXT = 'fa fa-forward';
|
||||||
|
|
|
@ -33,24 +33,24 @@ class UISettings extends Component {
|
||||||
];
|
];
|
||||||
|
|
||||||
const weekColumnOptions = [
|
const weekColumnOptions = [
|
||||||
{ key: 'ddd M/D', value: 'Tue 3/5' },
|
{ key: 'ddd M/D', value: 'Tue 3/25' },
|
||||||
{ key: 'ddd MM/DD', value: 'Tue 03/05' },
|
{ key: 'ddd MM/DD', value: 'Tue 03/25' },
|
||||||
{ key: 'ddd D/M', value: 'Tue 5/3' },
|
{ key: 'ddd D/M', value: 'Tue 25/3' },
|
||||||
{ key: 'ddd DD/MM', value: 'Tue 05/03' }
|
{ key: 'ddd DD/MM', value: 'Tue 25/03' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const shortDateFormatOptions = [
|
const shortDateFormatOptions = [
|
||||||
{ key: 'MMM D YYYY', value: 'Mar 5 2014' },
|
{ key: 'MMM D YYYY', value: 'Mar 25 2014' },
|
||||||
{ key: 'DD MMM YYYY', value: '5 Mar 2014' },
|
{ key: 'DD MMM YYYY', value: '25 Mar 2014' },
|
||||||
{ key: 'MM/D/YYYY', value: '03/5/2014' },
|
{ key: 'MM/D/YYYY', value: '03/25/2014' },
|
||||||
{ key: 'MM/DD/YYYY', value: '03/05/2014' },
|
{ key: 'MM/DD/YYYY', value: '03/25/2014' },
|
||||||
{ key: 'DD/MM/YYYY', value: '05/03/2014' },
|
{ key: 'DD/MM/YYYY', value: '25/03/2014' },
|
||||||
{ key: 'YYYY-MM-DD', value: '2014-03-05' }
|
{ key: 'YYYY-MM-DD', value: '2014-03-25' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const longDateFormatOptions = [
|
const longDateFormatOptions = [
|
||||||
{ key: 'dddd, MMMM D YYYY', value: 'Tuesday, March 5, 2014' },
|
{ key: 'dddd, MMMM D YYYY', value: 'Tuesday, March 25, 2014' },
|
||||||
{ key: 'dddd, D MMMM YYYY', value: 'Tuesday, 5 March, 2014' }
|
{ key: 'dddd, D MMMM YYYY', value: 'Tuesday, 25 March, 2014' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const timeFormatOptions = [
|
const timeFormatOptions = [
|
||||||
|
|
|
@ -55,6 +55,7 @@ export const SET_ARTIST_VIEW = 'SET_ARTIST_VIEW';
|
||||||
export const SET_ARTIST_TABLE_OPTION = 'SET_ARTIST_TABLE_OPTION';
|
export const SET_ARTIST_TABLE_OPTION = 'SET_ARTIST_TABLE_OPTION';
|
||||||
export const SET_ARTIST_POSTER_OPTION = 'SET_ARTIST_POSTER_OPTION';
|
export const SET_ARTIST_POSTER_OPTION = 'SET_ARTIST_POSTER_OPTION';
|
||||||
export const SET_ARTIST_BANNER_OPTION = 'SET_ARTIST_BANNER_OPTION';
|
export const SET_ARTIST_BANNER_OPTION = 'SET_ARTIST_BANNER_OPTION';
|
||||||
|
export const SET_ARTIST_OVERVIEW_OPTION = 'SET_ARTIST_OVERVIEW_OPTION';
|
||||||
|
|
||||||
export const TOGGLE_ARTIST_MONITORED = 'TOGGLE_ARTIST_MONITORED';
|
export const TOGGLE_ARTIST_MONITORED = 'TOGGLE_ARTIST_MONITORED';
|
||||||
export const TOGGLE_ALBUM_MONITORED = 'TOGGLE_ALBUM_MONITORED';
|
export const TOGGLE_ALBUM_MONITORED = 'TOGGLE_ALBUM_MONITORED';
|
||||||
|
|
|
@ -7,3 +7,4 @@ export const setArtistView = createAction(types.SET_ARTIST_VIEW);
|
||||||
export const setArtistTableOption = createAction(types.SET_ARTIST_TABLE_OPTION);
|
export const setArtistTableOption = createAction(types.SET_ARTIST_TABLE_OPTION);
|
||||||
export const setArtistPosterOption = createAction(types.SET_ARTIST_POSTER_OPTION);
|
export const setArtistPosterOption = createAction(types.SET_ARTIST_POSTER_OPTION);
|
||||||
export const setArtistBannerOption = createAction(types.SET_ARTIST_BANNER_OPTION);
|
export const setArtistBannerOption = createAction(types.SET_ARTIST_BANNER_OPTION);
|
||||||
|
export const setArtistOverviewOption = createAction(types.SET_ARTIST_OVERVIEW_OPTION);
|
||||||
|
|
|
@ -176,6 +176,10 @@ const calendarActionHandlers = {
|
||||||
|
|
||||||
[types.SET_CALENDAR_DAYS_COUNT]: function(payload) {
|
[types.SET_CALENDAR_DAYS_COUNT]: function(payload) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
if (payload.dayCount === getState().calendar.dayCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dispatch(set({
|
dispatch(set({
|
||||||
section,
|
section,
|
||||||
dayCount: payload.dayCount
|
dayCount: payload.dayCount
|
||||||
|
@ -184,10 +188,7 @@ const calendarActionHandlers = {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { time, view } = state.calendar;
|
const { time, view } = state.calendar;
|
||||||
|
|
||||||
dispatch(fetchCalendar({
|
dispatch(fetchCalendar({ time, view }));
|
||||||
time,
|
|
||||||
view
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -201,10 +202,7 @@ const calendarActionHandlers = {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { time, view } = state.calendar;
|
const { time, view } = state.calendar;
|
||||||
|
|
||||||
dispatch(fetchCalendar({
|
dispatch(fetchCalendar({ time, view }));
|
||||||
time,
|
|
||||||
view
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -212,7 +210,9 @@ const calendarActionHandlers = {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const view = payload.view;
|
const view = payload.view;
|
||||||
const time = view === calendarViews.FORECAST ? moment() : state.calendar.time;
|
const time = view === calendarViews.FORECAST ?
|
||||||
|
moment() :
|
||||||
|
state.calendar.time;
|
||||||
|
|
||||||
dispatch(fetchCalendar({ time, view }));
|
dispatch(fetchCalendar({ time, view }));
|
||||||
};
|
};
|
||||||
|
|
|
@ -206,7 +206,10 @@ const queueActionHandlers = {
|
||||||
});
|
});
|
||||||
|
|
||||||
promise.done((data) => {
|
promise.done((data) => {
|
||||||
dispatch(fetchQueue());
|
dispatch(batchActions([
|
||||||
|
set({ section, isRemoving: false }),
|
||||||
|
fetchQueue()
|
||||||
|
]));
|
||||||
});
|
});
|
||||||
|
|
||||||
promise.fail((xhr) => {
|
promise.fail((xhr) => {
|
||||||
|
|
|
@ -21,14 +21,26 @@ export const defaultState = {
|
||||||
detailedProgressBar: false,
|
detailedProgressBar: false,
|
||||||
size: 'large',
|
size: 'large',
|
||||||
showTitle: false,
|
showTitle: false,
|
||||||
showQualityProfile: false
|
showQualityProfile: true
|
||||||
},
|
},
|
||||||
|
|
||||||
bannerOptions: {
|
bannerOptions: {
|
||||||
detailedProgressBar: false,
|
detailedProgressBar: false,
|
||||||
size: 'large',
|
size: 'large',
|
||||||
showTitle: false,
|
showTitle: false,
|
||||||
showQualityProfile: false
|
showQualityProfile: true
|
||||||
|
},
|
||||||
|
|
||||||
|
overviewOptions: {
|
||||||
|
detailedProgressBar: false,
|
||||||
|
size: 'medium',
|
||||||
|
showNetwork: true,
|
||||||
|
showQualityProfile: true,
|
||||||
|
showPreviousAiring: false,
|
||||||
|
showAdded: false,
|
||||||
|
showAlbumCount: true,
|
||||||
|
showPath: false,
|
||||||
|
showSizeOnDisk: false
|
||||||
},
|
},
|
||||||
|
|
||||||
columns: [
|
columns: [
|
||||||
|
@ -175,7 +187,8 @@ export const persistState = [
|
||||||
'artistIndex.view',
|
'artistIndex.view',
|
||||||
'artistIndex.columns',
|
'artistIndex.columns',
|
||||||
'artistIndex.posterOptions',
|
'artistIndex.posterOptions',
|
||||||
'artistIndex.bannerOptions'
|
'artistIndex.bannerOptions',
|
||||||
|
'artistIndex.overviewOptions'
|
||||||
];
|
];
|
||||||
|
|
||||||
const reducerSection = 'artistIndex';
|
const reducerSection = 'artistIndex';
|
||||||
|
@ -215,6 +228,18 @@ const artistIndexReducers = handleActions({
|
||||||
...payload
|
...payload
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
[types.SET_ARTIST_OVERVIEW_OPTION]: function(state, { payload }) {
|
||||||
|
const overviewOptions = state.overviewOptions;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
overviewOptions: {
|
||||||
|
...overviewOptions,
|
||||||
|
...payload
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}, defaultState);
|
}, defaultState);
|
||||||
|
|
|
@ -36,8 +36,10 @@ module.exports = {
|
||||||
progressBarLargeHeight: '20px',
|
progressBarLargeHeight: '20px',
|
||||||
|
|
||||||
// Jump Bar
|
// Jump Bar
|
||||||
jumpBarItemHeight: '25px'
|
jumpBarItemHeight: '25px',
|
||||||
|
|
||||||
// Series
|
|
||||||
|
|
||||||
|
// Artist
|
||||||
|
artistIndexColumnPadding: '20px',
|
||||||
|
artistIndexColumnPaddingSmallScreen: '10px',
|
||||||
|
artistIndexOverviewInfoRowHeight: '21px'
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,5 +7,7 @@ module.exports = {
|
||||||
extraSmallFontSize: '11px',
|
extraSmallFontSize: '11px',
|
||||||
smallFontSize: '12px',
|
smallFontSize: '12px',
|
||||||
defaultFontSize: '14px',
|
defaultFontSize: '14px',
|
||||||
largeFontSize: '16px'
|
largeFontSize: '16px',
|
||||||
|
|
||||||
|
lineHeight: '1.528571429'
|
||||||
};
|
};
|
||||||
|
|
|
@ -77,8 +77,8 @@
|
||||||
"react-async-script": "0.9.1",
|
"react-async-script": "0.9.1",
|
||||||
"react-autosuggest": "9.3.2",
|
"react-autosuggest": "9.3.2",
|
||||||
"react-custom-scrollbars": "4.1.2",
|
"react-custom-scrollbars": "4.1.2",
|
||||||
"react-dnd": "2.5.3",
|
"react-dnd": "2.5.4",
|
||||||
"react-dnd-html5-backend": "2.5.3",
|
"react-dnd-html5-backend": "2.5.4",
|
||||||
"react-document-title": "2.0.3",
|
"react-document-title": "2.0.3",
|
||||||
"react-dom": "15.6.0",
|
"react-dom": "15.6.0",
|
||||||
"react-google-recaptcha": "0.9.7",
|
"react-google-recaptcha": "0.9.7",
|
||||||
|
@ -92,6 +92,7 @@
|
||||||
"react-tabs": "2.1.0",
|
"react-tabs": "2.1.0",
|
||||||
"react-tag-autocomplete": "5.4.1",
|
"react-tag-autocomplete": "5.4.1",
|
||||||
"react-tether": "0.5.7",
|
"react-tether": "0.5.7",
|
||||||
|
"react-truncate": "2.1.5",
|
||||||
"react-virtualized": "9.10.1",
|
"react-virtualized": "9.10.1",
|
||||||
"redux": "3.7.2",
|
"redux": "3.7.2",
|
||||||
"redux-actions": "2.2.1",
|
"redux-actions": "2.2.1",
|
||||||
|
|
|
@ -81,7 +81,7 @@ namespace Lidarr.Api.V3.Calendar
|
||||||
//occurrence.Description = album.Overview;
|
//occurrence.Description = album.Overview;
|
||||||
//occurrence.Categories = new List<string>() { album.Series.Network };
|
//occurrence.Categories = new List<string>() { album.Series.Network };
|
||||||
|
|
||||||
occurrence.Start = new CalDateTime(album.ReleaseDate.Value) { HasTime = false };
|
occurrence.Start = new CalDateTime(album.ReleaseDate.Value.ToLocalTime()) { HasTime = false };
|
||||||
|
|
||||||
occurrence.Summary = $"{album.Artist.Name} - {album.Title}";
|
occurrence.Summary = $"{album.Artist.Name} - {album.Title}";
|
||||||
}
|
}
|
||||||
|
|
24
yarn.lock
24
yarn.lock
|
@ -1921,9 +1921,9 @@ disposables@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/disposables/-/disposables-1.0.1.tgz#064727a25b54f502bd82b89aa2dfb8df9f1b39e3"
|
resolved "https://registry.yarnpkg.com/disposables/-/disposables-1.0.1.tgz#064727a25b54f502bd82b89aa2dfb8df9f1b39e3"
|
||||||
|
|
||||||
dnd-core@^2.5.3:
|
dnd-core@^2.5.4:
|
||||||
version "2.5.3"
|
version "2.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-2.5.3.tgz#a4163d59a01779860d2edbdadab5183ce321147c"
|
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-2.5.4.tgz#0c70a8dcbb609c0b222e275fcae9fa83e5897397"
|
||||||
dependencies:
|
dependencies:
|
||||||
asap "^2.0.6"
|
asap "^2.0.6"
|
||||||
invariant "^2.0.0"
|
invariant "^2.0.0"
|
||||||
|
@ -5246,18 +5246,18 @@ react-custom-scrollbars@4.1.2:
|
||||||
prop-types "^15.5.10"
|
prop-types "^15.5.10"
|
||||||
raf "^3.1.0"
|
raf "^3.1.0"
|
||||||
|
|
||||||
react-dnd-html5-backend@2.5.3:
|
react-dnd-html5-backend@2.5.4:
|
||||||
version "2.5.3"
|
version "2.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-2.5.3.tgz#1158112e8129dfe672d4c72222f266dd05321f03"
|
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-2.5.4.tgz#974ad083f67b12d56977a5b171f5ffeb29d78352"
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash "^4.2.0"
|
lodash "^4.2.0"
|
||||||
|
|
||||||
react-dnd@2.5.3:
|
react-dnd@2.5.4:
|
||||||
version "2.5.3"
|
version "2.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-2.5.3.tgz#ee5357da19d4ebd0e0da0a070a862bbc133e3241"
|
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-2.5.4.tgz#0b6dc5e9d0dfc2909f4f4fe736e5534f3afd1bd9"
|
||||||
dependencies:
|
dependencies:
|
||||||
disposables "^1.0.1"
|
disposables "^1.0.1"
|
||||||
dnd-core "^2.5.3"
|
dnd-core "^2.5.4"
|
||||||
hoist-non-react-statics "^2.1.0"
|
hoist-non-react-statics "^2.1.0"
|
||||||
invariant "^2.1.0"
|
invariant "^2.1.0"
|
||||||
lodash "^4.2.0"
|
lodash "^4.2.0"
|
||||||
|
@ -5382,6 +5382,10 @@ react-themeable@^1.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
object-assign "^3.0.0"
|
object-assign "^3.0.0"
|
||||||
|
|
||||||
|
react-truncate@2.1.5:
|
||||||
|
version "2.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-truncate/-/react-truncate-2.1.5.tgz#c33c1bf7053c2800d4012fefab09a18013019721"
|
||||||
|
|
||||||
react-virtualized@9.10.1:
|
react-virtualized@9.10.1:
|
||||||
version "9.10.1"
|
version "9.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.10.1.tgz#d32365d0edf49debbe25fbfe73b5f55f6d9d8c72"
|
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.10.1.tgz#d32365d0edf49debbe25fbfe73b5f55f6d9d8c72"
|
||||||
|
|
Loading…
Reference in New Issue