mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-25 22:46:31 -04:00
Added series index selection
This commit is contained in:
committed by
Mark McDowall
parent
5aad84dba4
commit
815a16d5cf
@@ -9,6 +9,7 @@ import { icons } from 'Helpers/Props';
|
||||
import DeleteSeriesModal from 'Series/Delete/DeleteSeriesModal';
|
||||
import EditSeriesModalConnector from 'Series/Edit/EditSeriesModalConnector';
|
||||
import SeriesIndexProgressBar from 'Series/Index/ProgressBar/SeriesIndexProgressBar';
|
||||
import SeriesIndexPosterSelect from 'Series/Index/Select/SeriesIndexPosterSelect';
|
||||
import SeriesPoster from 'Series/SeriesPoster';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import dimensions from 'Styles/Variables/dimensions';
|
||||
@@ -35,6 +36,7 @@ interface SeriesIndexOverviewProps {
|
||||
posterWidth: number;
|
||||
posterHeight: number;
|
||||
rowHeight: number;
|
||||
isSelectMode: boolean;
|
||||
isSmallScreen: boolean;
|
||||
}
|
||||
|
||||
@@ -45,6 +47,7 @@ function SeriesIndexOverview(props: SeriesIndexOverviewProps) {
|
||||
posterWidth,
|
||||
posterHeight,
|
||||
rowHeight,
|
||||
isSelectMode,
|
||||
isSmallScreen,
|
||||
} = props;
|
||||
|
||||
@@ -135,6 +138,10 @@ function SeriesIndexOverview(props: SeriesIndexOverviewProps) {
|
||||
<div className={styles.content}>
|
||||
<div className={styles.poster}>
|
||||
<div className={styles.posterContainer}>
|
||||
{isSelectMode ? (
|
||||
<SeriesIndexPosterSelect seriesId={seriesId} />
|
||||
) : null}
|
||||
|
||||
{status === 'ended' && (
|
||||
<div className={styles.ended} title="Ended" />
|
||||
)}
|
||||
|
||||
@@ -27,6 +27,7 @@ interface RowItemData {
|
||||
posterWidth: number;
|
||||
posterHeight: number;
|
||||
rowHeight: number;
|
||||
isSelectMode: boolean;
|
||||
isSmallScreen: boolean;
|
||||
}
|
||||
|
||||
@@ -37,6 +38,7 @@ interface SeriesIndexOverviewsProps {
|
||||
jumpToCharacter?: string;
|
||||
scrollTop?: number;
|
||||
scrollerRef: React.MutableRefObject<HTMLElement>;
|
||||
isSelectMode: boolean;
|
||||
isSmallScreen: boolean;
|
||||
}
|
||||
|
||||
@@ -65,7 +67,14 @@ function getWindowScrollTopPosition() {
|
||||
}
|
||||
|
||||
function SeriesIndexOverviews(props: SeriesIndexOverviewsProps) {
|
||||
const { items, sortKey, jumpToCharacter, isSmallScreen, scrollerRef } = props;
|
||||
const {
|
||||
items,
|
||||
sortKey,
|
||||
jumpToCharacter,
|
||||
scrollerRef,
|
||||
isSelectMode,
|
||||
isSmallScreen,
|
||||
} = props;
|
||||
|
||||
const { size: posterSize, detailedProgressBar } = useSelector(
|
||||
selectOverviewOptions
|
||||
@@ -191,6 +200,7 @@ function SeriesIndexOverviews(props: SeriesIndexOverviewsProps) {
|
||||
posterWidth,
|
||||
posterHeight,
|
||||
rowHeight,
|
||||
isSelectMode,
|
||||
isSmallScreen,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { icons } from 'Helpers/Props';
|
||||
import DeleteSeriesModal from 'Series/Delete/DeleteSeriesModal';
|
||||
import EditSeriesModalConnector from 'Series/Edit/EditSeriesModalConnector';
|
||||
import SeriesIndexProgressBar from 'Series/Index/ProgressBar/SeriesIndexProgressBar';
|
||||
import SeriesIndexPosterSelect from 'Series/Index/Select/SeriesIndexPosterSelect';
|
||||
import SeriesPoster from 'Series/SeriesPoster';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||
@@ -21,12 +22,13 @@ import styles from './SeriesIndexPoster.css';
|
||||
interface SeriesIndexPosterProps {
|
||||
seriesId: number;
|
||||
sortKey: string;
|
||||
isSelectMode: boolean;
|
||||
posterWidth: number;
|
||||
posterHeight: number;
|
||||
}
|
||||
|
||||
function SeriesIndexPoster(props: SeriesIndexPosterProps) {
|
||||
const { seriesId, sortKey, posterWidth, posterHeight } = props;
|
||||
const { seriesId, sortKey, isSelectMode, posterWidth, posterHeight } = props;
|
||||
|
||||
const { series, qualityProfile, isRefreshingSeries, isSearchingSeries } =
|
||||
useSelector(createSeriesIndexItemSelector(props.seriesId));
|
||||
@@ -120,6 +122,8 @@ function SeriesIndexPoster(props: SeriesIndexPosterProps) {
|
||||
return (
|
||||
<div className={styles.content}>
|
||||
<div className={styles.posterContainer}>
|
||||
{isSelectMode ? <SeriesIndexPosterSelect seriesId={seriesId} /> : null}
|
||||
|
||||
<Label className={styles.controls}>
|
||||
<SpinnerIconButton
|
||||
className={styles.action}
|
||||
|
||||
@@ -36,6 +36,7 @@ interface CellItemData {
|
||||
};
|
||||
items: Series[];
|
||||
sortKey: string;
|
||||
isSelectMode: boolean;
|
||||
}
|
||||
|
||||
interface SeriesIndexPostersProps {
|
||||
@@ -45,6 +46,7 @@ interface SeriesIndexPostersProps {
|
||||
jumpToCharacter?: string;
|
||||
scrollTop?: number;
|
||||
scrollerRef: React.MutableRefObject<HTMLElement>;
|
||||
isSelectMode: boolean;
|
||||
isSmallScreen: boolean;
|
||||
}
|
||||
|
||||
@@ -63,10 +65,8 @@ const Cell: React.FC<GridChildComponentProps<CellItemData>> = ({
|
||||
style,
|
||||
data,
|
||||
}) => {
|
||||
const { layout, items, sortKey } = data;
|
||||
|
||||
const { layout, items, sortKey, isSelectMode } = data;
|
||||
const { columnCount, padding, posterWidth, posterHeight } = layout;
|
||||
|
||||
const index = rowIndex * columnCount + columnIndex;
|
||||
|
||||
if (index >= items.length) {
|
||||
@@ -85,6 +85,7 @@ const Cell: React.FC<GridChildComponentProps<CellItemData>> = ({
|
||||
<SeriesIndexPoster
|
||||
seriesId={series.id}
|
||||
sortKey={sortKey}
|
||||
isSelectMode={isSelectMode}
|
||||
posterWidth={posterWidth}
|
||||
posterHeight={posterHeight}
|
||||
/>
|
||||
@@ -97,7 +98,14 @@ function getWindowScrollTopPosition() {
|
||||
}
|
||||
|
||||
export default function SeriesIndexPosters(props: SeriesIndexPostersProps) {
|
||||
const { scrollerRef, items, sortKey, jumpToCharacter, isSmallScreen } = props;
|
||||
const {
|
||||
scrollerRef,
|
||||
items,
|
||||
sortKey,
|
||||
jumpToCharacter,
|
||||
isSelectMode,
|
||||
isSmallScreen,
|
||||
} = props;
|
||||
|
||||
const { posterOptions } = useSelector(seriesIndexSelector);
|
||||
const ref: React.MutableRefObject<Grid> = useRef();
|
||||
@@ -273,6 +281,7 @@ export default function SeriesIndexPosters(props: SeriesIndexPostersProps) {
|
||||
},
|
||||
items,
|
||||
sortKey,
|
||||
isSelectMode,
|
||||
}}
|
||||
>
|
||||
{Cell}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
.checkContainer {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
z-index: 3;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--defaultColor);
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
}
|
||||
|
||||
.selected {
|
||||
composes: icon;
|
||||
|
||||
color: var(--sonarrBlue);
|
||||
|
||||
&:hover {
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
.unselected {
|
||||
composes: icon;
|
||||
|
||||
color: var(--white);
|
||||
|
||||
&:hover {
|
||||
color: var(--sonarrBlue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { SelectActionType, useSelect } from 'App/SelectContext';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import styles from './SeriesIndexPosterSelect.css';
|
||||
|
||||
interface SeriesIndexPosterSelectProps {
|
||||
seriesId: number;
|
||||
}
|
||||
|
||||
function SeriesIndexPosterSelect(props: SeriesIndexPosterSelectProps) {
|
||||
const { seriesId } = props;
|
||||
const [selectState, selectDispatch] = useSelect();
|
||||
const isSelected = selectState.selectedState[seriesId];
|
||||
|
||||
const onSelectPress = useCallback(
|
||||
(event) => {
|
||||
const shiftKey = event.nativeEvent.shiftKey;
|
||||
|
||||
selectDispatch({
|
||||
type: SelectActionType.ToggleSelected,
|
||||
id: seriesId,
|
||||
isSelected: !isSelected,
|
||||
shiftKey,
|
||||
});
|
||||
},
|
||||
[seriesId, isSelected, selectDispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className={styles.checkContainer}
|
||||
iconClassName={isSelected ? styles.selected : styles.unselected}
|
||||
name={isSelected ? icons.CHECK_CIRCLE : icons.CIRCLE_OUTLINE}
|
||||
size={20}
|
||||
onPress={onSelectPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default SeriesIndexPosterSelect;
|
||||
@@ -0,0 +1,35 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { SelectActionType, useSelect } from 'App/SelectContext';
|
||||
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
|
||||
import { icons } from 'Helpers/Props';
|
||||
|
||||
function SeriesIndexSelectAllButton() {
|
||||
const [selectState, selectDispatch] = useSelect();
|
||||
const { allSelected, allUnselected } = selectState;
|
||||
|
||||
let icon = icons.SQUARE_MINUS;
|
||||
|
||||
if (allSelected) {
|
||||
icon = icons.CHECK_SQUARE;
|
||||
} else if (allUnselected) {
|
||||
icon = icons.SQUARE;
|
||||
}
|
||||
|
||||
const onPress = useCallback(() => {
|
||||
selectDispatch({
|
||||
type: allSelected
|
||||
? SelectActionType.UnselectAll
|
||||
: SelectActionType.SelectAll,
|
||||
});
|
||||
}, [allSelected, selectDispatch]);
|
||||
|
||||
return (
|
||||
<PageToolbarButton
|
||||
label={allSelected ? 'Unselect All' : 'Select All'}
|
||||
iconName={icon}
|
||||
onPress={onPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default SeriesIndexSelectAllButton;
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { SelectProvider } from 'App/SelectContext';
|
||||
import { REFRESH_SERIES, RSS_SYNC } from 'Commands/commandNames';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
@@ -32,6 +33,7 @@ import SeriesIndexOverviewOptionsModal from './Overview/Options/SeriesIndexOverv
|
||||
import SeriesIndexOverviews from './Overview/SeriesIndexOverviews';
|
||||
import SeriesIndexPosterOptionsModal from './Posters/Options/SeriesIndexPosterOptionsModal';
|
||||
import SeriesIndexPosters from './Posters/SeriesIndexPosters';
|
||||
import SeriesIndexSelectAllButton from './Select/SeriesIndexSelectAllButton';
|
||||
import SeriesIndexFooter from './SeriesIndexFooter';
|
||||
import SeriesIndexTable from './Table/SeriesIndexTable';
|
||||
import SeriesIndexTableOptions from './Table/SeriesIndexTableOptions';
|
||||
@@ -53,7 +55,7 @@ interface SeriesIndexProps {
|
||||
initialScrollTop?: number;
|
||||
}
|
||||
|
||||
const SeriesIndex = withScrollPosition((props) => {
|
||||
const SeriesIndex = withScrollPosition((props: SeriesIndexProps) => {
|
||||
const {
|
||||
isFetching,
|
||||
isPopulated,
|
||||
@@ -80,6 +82,7 @@ const SeriesIndex = withScrollPosition((props) => {
|
||||
const scrollerRef = useRef<HTMLDivElement>();
|
||||
const [isOptionsModalOpen, setIsOptionsModalOpen] = useState(false);
|
||||
const [jumpToCharacter, setJumpToCharacter] = useState<string | null>(null);
|
||||
const [isSelectMode, setIsSelectMode] = useState(false);
|
||||
|
||||
const onRefreshSeriesPress = useCallback(() => {
|
||||
dispatch(
|
||||
@@ -97,6 +100,10 @@ const SeriesIndex = withScrollPosition((props) => {
|
||||
);
|
||||
}, [dispatch]);
|
||||
|
||||
const onSelectModePress = useCallback(() => {
|
||||
setIsSelectMode(!isSelectMode);
|
||||
}, [isSelectMode, setIsSelectMode]);
|
||||
|
||||
const onTableOptionChange = useCallback(
|
||||
(payload) => {
|
||||
dispatch(setSeriesTableOption(payload));
|
||||
@@ -194,118 +201,137 @@ const SeriesIndex = withScrollPosition((props) => {
|
||||
const hasNoSeries = !totalItems;
|
||||
|
||||
return (
|
||||
<PageContent>
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label="Update all"
|
||||
iconName={icons.REFRESH}
|
||||
spinningName={icons.REFRESH}
|
||||
isSpinning={isRefreshingSeries}
|
||||
isDisabled={hasNoSeries}
|
||||
onPress={onRefreshSeriesPress}
|
||||
/>
|
||||
|
||||
<PageToolbarButton
|
||||
label="RSS Sync"
|
||||
iconName={icons.RSS}
|
||||
isSpinning={isRssSyncExecuting}
|
||||
isDisabled={hasNoSeries}
|
||||
onPress={onRssSyncPress}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
|
||||
<PageToolbarSection alignContent={align.RIGHT} collapseButtons={false}>
|
||||
{view === 'table' ? (
|
||||
<TableOptionsModalWrapper
|
||||
columns={columns}
|
||||
optionsComponent={SeriesIndexTableOptions}
|
||||
onTableOptionChange={onTableOptionChange}
|
||||
>
|
||||
<PageToolbarButton label="Options" iconName={icons.TABLE} />
|
||||
</TableOptionsModalWrapper>
|
||||
) : (
|
||||
<SelectProvider isSelectMode={isSelectMode} items={items}>
|
||||
<PageContent>
|
||||
<PageToolbar>
|
||||
<PageToolbarSection>
|
||||
<PageToolbarButton
|
||||
label="Options"
|
||||
iconName={view === 'posters' ? icons.POSTER : icons.OVERVIEW}
|
||||
label="Update all"
|
||||
iconName={icons.REFRESH}
|
||||
spinningName={icons.REFRESH}
|
||||
isSpinning={isRefreshingSeries}
|
||||
isDisabled={hasNoSeries}
|
||||
onPress={onOptionsPress}
|
||||
onPress={onRefreshSeriesPress}
|
||||
/>
|
||||
)}
|
||||
|
||||
<PageToolbarSeparator />
|
||||
<PageToolbarButton
|
||||
label="RSS Sync"
|
||||
iconName={icons.RSS}
|
||||
isSpinning={isRssSyncExecuting}
|
||||
isDisabled={hasNoSeries}
|
||||
onPress={onRssSyncPress}
|
||||
/>
|
||||
|
||||
<SeriesIndexViewMenu
|
||||
view={view}
|
||||
isDisabled={hasNoSeries}
|
||||
onViewSelect={onViewSelect}
|
||||
/>
|
||||
<PageToolbarSeparator />
|
||||
|
||||
<SeriesIndexSortMenu
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
isDisabled={hasNoSeries}
|
||||
onSortSelect={onSortSelect}
|
||||
/>
|
||||
<PageToolbarButton
|
||||
label={isSelectMode ? 'Stop Selecting' : 'Select Series'}
|
||||
iconName={isSelectMode ? icons.SERIES_ENDED : icons.CHECK}
|
||||
onPress={onSelectModePress}
|
||||
/>
|
||||
|
||||
<SeriesIndexFilterMenu
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
filters={filters}
|
||||
customFilters={customFilters}
|
||||
isDisabled={hasNoSeries}
|
||||
onFilterSelect={onFilterSelect}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
</PageToolbar>
|
||||
<div className={styles.pageContentBodyWrapper}>
|
||||
<PageContentBody
|
||||
ref={scrollerRef}
|
||||
className={styles.contentBody}
|
||||
innerClassName={styles[`${view}InnerContentBody`]}
|
||||
initialScrollTop={props.initialScrollTop}
|
||||
onScroll={onScroll}
|
||||
>
|
||||
{isFetching && !isPopulated ? <LoadingIndicator /> : null}
|
||||
{isSelectMode ? <SeriesIndexSelectAllButton /> : null}
|
||||
</PageToolbarSection>
|
||||
|
||||
{!isFetching && !!error ? <div>Unable to load series</div> : null}
|
||||
|
||||
{isLoaded ? (
|
||||
<div className={styles.contentBodyContainer}>
|
||||
<ViewComponent
|
||||
scrollerRef={scrollerRef}
|
||||
items={items}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
jumpToCharacter={jumpToCharacter}
|
||||
isSmallScreen={isSmallScreen}
|
||||
<PageToolbarSection
|
||||
alignContent={align.RIGHT}
|
||||
collapseButtons={false}
|
||||
>
|
||||
{view === 'table' ? (
|
||||
<TableOptionsModalWrapper
|
||||
columns={columns}
|
||||
optionsComponent={SeriesIndexTableOptions}
|
||||
onTableOptionChange={onTableOptionChange}
|
||||
>
|
||||
<PageToolbarButton label="Options" iconName={icons.TABLE} />
|
||||
</TableOptionsModalWrapper>
|
||||
) : (
|
||||
<PageToolbarButton
|
||||
label="Options"
|
||||
iconName={view === 'posters' ? icons.POSTER : icons.OVERVIEW}
|
||||
isDisabled={hasNoSeries}
|
||||
onPress={onOptionsPress}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SeriesIndexFooter />
|
||||
</div>
|
||||
<PageToolbarSeparator />
|
||||
|
||||
<SeriesIndexViewMenu
|
||||
view={view}
|
||||
isDisabled={hasNoSeries}
|
||||
onViewSelect={onViewSelect}
|
||||
/>
|
||||
|
||||
<SeriesIndexSortMenu
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
isDisabled={hasNoSeries}
|
||||
onSortSelect={onSortSelect}
|
||||
/>
|
||||
|
||||
<SeriesIndexFilterMenu
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
filters={filters}
|
||||
customFilters={customFilters}
|
||||
isDisabled={hasNoSeries}
|
||||
onFilterSelect={onFilterSelect}
|
||||
/>
|
||||
</PageToolbarSection>
|
||||
</PageToolbar>
|
||||
<div className={styles.pageContentBodyWrapper}>
|
||||
<PageContentBody
|
||||
ref={scrollerRef}
|
||||
className={styles.contentBody}
|
||||
innerClassName={styles[`${view}InnerContentBody`]}
|
||||
initialScrollTop={props.initialScrollTop}
|
||||
onScroll={onScroll}
|
||||
>
|
||||
{isFetching && !isPopulated ? <LoadingIndicator /> : null}
|
||||
|
||||
{!isFetching && !!error ? <div>Unable to load series</div> : null}
|
||||
|
||||
{isLoaded ? (
|
||||
<div className={styles.contentBodyContainer}>
|
||||
<ViewComponent
|
||||
scrollerRef={scrollerRef}
|
||||
items={items}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
jumpToCharacter={jumpToCharacter}
|
||||
isSelectMode={isSelectMode}
|
||||
isSmallScreen={isSmallScreen}
|
||||
/>
|
||||
|
||||
<SeriesIndexFooter />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!error && isPopulated && !items.length ? (
|
||||
<NoSeries totalItems={totalItems} />
|
||||
) : null}
|
||||
</PageContentBody>
|
||||
|
||||
{isLoaded && !!jumpBarItems.order.length ? (
|
||||
<PageJumpBar
|
||||
items={jumpBarItems}
|
||||
onItemPress={onJumpBarItemPress}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{!error && isPopulated && !items.length ? (
|
||||
<NoSeries totalItems={totalItems} />
|
||||
) : null}
|
||||
</PageContentBody>
|
||||
|
||||
{isLoaded && !!jumpBarItems.order.length ? (
|
||||
<PageJumpBar items={jumpBarItems} onItemPress={onJumpBarItemPress} />
|
||||
</div>
|
||||
{view === 'posters' ? (
|
||||
<SeriesIndexPosterOptionsModal
|
||||
isOpen={isOptionsModalOpen}
|
||||
onModalClose={onOptionsModalClose}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
{view === 'posters' ? (
|
||||
<SeriesIndexPosterOptionsModal
|
||||
isOpen={isOptionsModalOpen}
|
||||
onModalClose={onOptionsModalClose}
|
||||
/>
|
||||
) : null}
|
||||
{view === 'overview' ? (
|
||||
<SeriesIndexOverviewOptionsModal
|
||||
isOpen={isOptionsModalOpen}
|
||||
onModalClose={onOptionsModalClose}
|
||||
/>
|
||||
) : null}
|
||||
</PageContent>
|
||||
{view === 'overview' ? (
|
||||
<SeriesIndexOverviewOptionsModal
|
||||
isOpen={isOptionsModalOpen}
|
||||
onModalClose={onOptionsModalClose}
|
||||
/>
|
||||
) : null}
|
||||
</PageContent>
|
||||
</SelectProvider>
|
||||
);
|
||||
}, 'seriesIndex');
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||
import ProgressBar from 'Components/ProgressBar';
|
||||
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
|
||||
import VirtualTableRowCell from 'Components/Table/Cells/VirtualTableRowCell';
|
||||
import VirtualTableSelectCell from 'Components/Table/Cells/VirtualTableSelectCell';
|
||||
import Column from 'Components/Table/Column';
|
||||
import TagListConnector from 'Components/TagListConnector';
|
||||
import { icons } from 'Helpers/Props';
|
||||
@@ -32,10 +33,11 @@ interface SeriesIndexRowProps {
|
||||
seriesId: number;
|
||||
sortKey: string;
|
||||
columns: Column[];
|
||||
isSelectMode: boolean;
|
||||
}
|
||||
|
||||
function SeriesIndexRow(props: SeriesIndexRowProps) {
|
||||
const { seriesId, columns } = props;
|
||||
const { seriesId, columns, isSelectMode } = props;
|
||||
|
||||
const {
|
||||
series,
|
||||
@@ -82,6 +84,7 @@ function SeriesIndexRow(props: SeriesIndexRowProps) {
|
||||
const [hasBannerError, setHasBannerError] = useState(false);
|
||||
const [isEditSeriesModalOpen, setIsEditSeriesModalOpen] = useState(false);
|
||||
const [isDeleteSeriesModalOpen, setIsDeleteSeriesModalOpen] = useState(false);
|
||||
const [selectState, selectDispatch] = useSelect();
|
||||
|
||||
const onRefreshPress = useCallback(() => {
|
||||
dispatch(
|
||||
@@ -130,8 +133,29 @@ function SeriesIndexRow(props: SeriesIndexRowProps) {
|
||||
// Mock handler to satisfy `onChange` being required for `CheckInput`.
|
||||
}, []);
|
||||
|
||||
const onSelectedChange = useCallback(
|
||||
({ id, value, shiftKey }) => {
|
||||
selectDispatch({
|
||||
type: SelectActionType.ToggleSelected,
|
||||
id,
|
||||
isSelected: value,
|
||||
shiftKey,
|
||||
});
|
||||
},
|
||||
[selectDispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isSelectMode ? (
|
||||
<VirtualTableSelectCell
|
||||
id={seriesId}
|
||||
isSelected={selectState.selectedState[seriesId]}
|
||||
isDisabled={false}
|
||||
onSelectedChange={onSelectedChange}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{columns.map((column) => {
|
||||
const { name, isVisible } = column;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ interface RowItemData {
|
||||
items: Series[];
|
||||
sortKey: string;
|
||||
columns: Column[];
|
||||
isSelectMode: boolean;
|
||||
}
|
||||
|
||||
interface SeriesIndexTableProps {
|
||||
@@ -34,6 +35,7 @@ interface SeriesIndexTableProps {
|
||||
jumpToCharacter?: string;
|
||||
scrollTop?: number;
|
||||
scrollerRef: React.MutableRefObject<HTMLElement>;
|
||||
isSelectMode: boolean;
|
||||
isSmallScreen: boolean;
|
||||
}
|
||||
|
||||
@@ -47,7 +49,7 @@ const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
|
||||
style,
|
||||
data,
|
||||
}) => {
|
||||
const { items, sortKey, columns } = data;
|
||||
const { items, sortKey, columns, isSelectMode } = data;
|
||||
|
||||
if (index >= items.length) {
|
||||
return null;
|
||||
@@ -67,6 +69,7 @@ const Row: React.FC<ListChildComponentProps<RowItemData>> = ({
|
||||
seriesId={series.id}
|
||||
sortKey={sortKey}
|
||||
columns={columns}
|
||||
isSelectMode={isSelectMode}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -82,6 +85,7 @@ function SeriesIndexTable(props: SeriesIndexTableProps) {
|
||||
sortKey,
|
||||
sortDirection,
|
||||
jumpToCharacter,
|
||||
isSelectMode,
|
||||
isSmallScreen,
|
||||
scrollerRef,
|
||||
} = props;
|
||||
@@ -177,6 +181,7 @@ function SeriesIndexTable(props: SeriesIndexTableProps) {
|
||||
columns={columns}
|
||||
sortKey={sortKey}
|
||||
sortDirection={sortDirection}
|
||||
isSelectMode={isSelectMode}
|
||||
/>
|
||||
<List<RowItemData>
|
||||
ref={listRef}
|
||||
@@ -193,6 +198,7 @@ function SeriesIndexTable(props: SeriesIndexTableProps) {
|
||||
items,
|
||||
sortKey,
|
||||
columns,
|
||||
isSelectMode,
|
||||
}}
|
||||
>
|
||||
{Row}
|
||||
|
||||
@@ -7,6 +7,7 @@ import Column from 'Components/Table/Column';
|
||||
import TableOptionsModalWrapper from 'Components/Table/TableOptions/TableOptionsModalWrapper';
|
||||
import VirtualTableHeader from 'Components/Table/VirtualTableHeader';
|
||||
import VirtualTableHeaderCell from 'Components/Table/VirtualTableHeaderCell';
|
||||
import VirtualTableSelectAllHeaderCell from 'Components/Table/VirtualTableSelectAllHeaderCell';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import SortDirection from 'Helpers/Props/SortDirection';
|
||||
import {
|
||||
@@ -22,12 +23,13 @@ interface SeriesIndexTableHeaderProps {
|
||||
columns: Column[];
|
||||
sortKey?: string;
|
||||
sortDirection?: SortDirection;
|
||||
isSelectMode: boolean;
|
||||
}
|
||||
|
||||
function SeriesIndexTableHeader(props: SeriesIndexTableHeaderProps) {
|
||||
const { showBanners, columns, sortKey, sortDirection } = props;
|
||||
|
||||
const { showBanners, columns, sortKey, sortDirection, isSelectMode } = props;
|
||||
const dispatch = useDispatch();
|
||||
const [selectState, selectDispatch] = useSelect();
|
||||
|
||||
const onSortPress = useCallback(
|
||||
(value) => {
|
||||
@@ -43,8 +45,25 @@ function SeriesIndexTableHeader(props: SeriesIndexTableHeaderProps) {
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const onSelectAllChange = useCallback(
|
||||
({ value }) => {
|
||||
selectDispatch({
|
||||
type: value ? SelectActionType.SelectAll : SelectActionType.UnselectAll,
|
||||
});
|
||||
},
|
||||
[selectDispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<VirtualTableHeader>
|
||||
{isSelectMode ? (
|
||||
<VirtualTableSelectAllHeaderCell
|
||||
allSelected={selectState.allSelected}
|
||||
allUnselected={selectState.allUnselected}
|
||||
onSelectAllChange={onSelectAllChange}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{columns.map((column) => {
|
||||
const { name, label, isSortable, isVisible } = column;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user