+ {
+ !!nextAiring &&
+
+
+
+ {
+ getRelativeDate(
+ nextAiring,
+ shortDateFormat,
+ showRelativeDates,
+ {
+ timeFormat,
+ timeForToday: true
+ }
+ )
+ }
+
+ }
+
+ {
+ isVisible('qualityProfileId', showQualityProfile, qualityProfile, sortKey) && maxRows > 1 &&
+
+
+
+ {qualityProfile.name}
+
+ }
+
+ {
+ isVisible('added', showAdded, added, sortKey) && maxRows > 2 &&
+
+
+
+ {
+ getRelativeDate(
+ added,
+ shortDateFormat,
+ showRelativeDates,
+ {
+ timeFormat,
+ timeForToday: true
+ }
+ )
+ }
+
+ }
+
+ {
+ isVisible('albumCount', showAlbumCount, albumCount, sortKey) && maxRows > 3 &&
+
+
+
+ {albums}
+
+ }
+
+ {
+ isVisible('path', showPath, path, sortKey) && maxRows > 4 &&
+
+
+
+ {path}
+
+ }
+
+ {
+ isVisible('sizeOnDisk', showSizeOnDisk, sizeOnDisk, sortKey) && maxRows > 5 &&
+
+
+
+ {formatBytes(sizeOnDisk)}
+
+ }
+
+
+ );
+}
+
+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;
diff --git a/frontend/src/Artist/Index/Overview/ArtistIndexOverviews.css b/frontend/src/Artist/Index/Overview/ArtistIndexOverviews.css
new file mode 100644
index 000000000..9c6520fb5
--- /dev/null
+++ b/frontend/src/Artist/Index/Overview/ArtistIndexOverviews.css
@@ -0,0 +1,3 @@
+.grid {
+ flex: 1 0 auto;
+}
diff --git a/frontend/src/Artist/Index/Overview/ArtistIndexOverviews.js b/frontend/src/Artist/Index/Overview/ArtistIndexOverviews.js
new file mode 100644
index 000000000..801a9d35b
--- /dev/null
+++ b/frontend/src/Artist/Index/Overview/ArtistIndexOverviews.js
@@ -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 (
+