1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-24 22:36:19 -04:00

Convert Import Series to TypeScript

This commit is contained in:
Mark McDowall
2025-01-12 19:39:29 -08:00
parent ab1f8bdbd9
commit 6f871a1bfb
54 changed files with 1614 additions and 2320 deletions
+98 -143
View File
@@ -1,166 +1,121 @@
import React, { ReactNode, useEffect, useRef } from 'react';
import { Grid, GridCellProps, WindowScroller } from 'react-virtualized';
import ModelBase from 'App/ModelBase';
import { throttle } from 'lodash';
import React, { RefObject, useEffect, useState } from 'react';
import { FixedSizeList, ListChildComponentProps } from 'react-window';
import Scroller from 'Components/Scroller/Scroller';
import useMeasure from 'Helpers/Hooks/useMeasure';
import usePrevious from 'Helpers/Hooks/usePrevious';
import { scrollDirections } from 'Helpers/Props';
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
import dimensions from 'Styles/Variables/dimensions';
import styles from './VirtualTable.css';
const ROW_HEIGHT = 38;
const bodyPadding = parseInt(dimensions.pageContentBodyPadding);
const bodyPaddingSmallScreen = parseInt(
dimensions.pageContentBodyPaddingSmallScreen
);
function overscanIndicesGetter(options: {
cellCount: number;
overscanCellsCount: number;
startIndex: number;
stopIndex: number;
}) {
const { cellCount, overscanCellsCount, startIndex, stopIndex } = options;
// The default getter takes the scroll direction into account,
// but that can cause issues. Ignore the scroll direction and
// always over return more items.
const overscanStartIndex = startIndex - overscanCellsCount;
const overscanStopIndex = stopIndex + overscanCellsCount;
return {
overscanStartIndex: Math.max(0, overscanStartIndex),
overscanStopIndex: Math.min(cellCount - 1, overscanStopIndex),
};
}
interface VirtualTableProps<T extends ModelBase> {
interface VirtualTableProps<T> {
Header: React.JSX.Element;
itemCount: number;
itemData: T;
isSmallScreen: boolean;
className?: string;
items: T[];
scrollIndex?: number;
scrollTop?: number;
scroller: Element;
header: React.ReactNode;
headerHeight?: number;
rowRenderer: (rowProps: GridCellProps) => ReactNode;
rowHeight?: number;
listRef: RefObject<FixedSizeList<T>>;
rowHeight: number;
Row({
index,
style,
data,
}: ListChildComponentProps<T>): React.JSX.Element | null;
scrollerRef: RefObject<HTMLElement>;
}
function VirtualTable<T extends ModelBase>({
function getWindowScrollTopPosition() {
return document.documentElement.scrollTop || document.body.scrollTop || 0;
}
function VirtualTable<T>({
Header,
itemCount,
itemData,
isSmallScreen,
className = styles.tableContainer,
items,
scroller,
scrollIndex,
scrollTop,
header,
headerHeight = 38,
rowHeight = ROW_HEIGHT,
rowRenderer,
...otherProps
listRef,
rowHeight,
Row,
scrollerRef,
}: VirtualTableProps<T>) {
const [measureRef, bounds] = useMeasure();
const gridRef = useRef<Grid>(null);
const scrollRestored = useRef(false);
const previousScrollIndex = usePrevious(scrollIndex);
const previousItems = usePrevious(items);
const width = bounds.width;
const gridStyle = {
boxSizing: undefined,
direction: undefined,
height: undefined,
position: undefined,
willChange: undefined,
overflow: undefined,
width: undefined,
};
const containerStyle = {
position: undefined,
};
const [size, setSize] = useState({ width: 0, height: 0 });
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
useEffect(() => {
if (gridRef.current && width > 0) {
gridRef.current.recomputeGridSize();
const current = scrollerRef?.current as HTMLElement;
if (isSmallScreen) {
setSize({
width: windowWidth,
height: windowHeight,
});
return;
}
}, [width]);
useEffect(() => {
if (
gridRef.current &&
previousItems &&
hasDifferentItemsOrOrder(previousItems, items)
) {
gridRef.current.recomputeGridSize();
}
}, [items, previousItems]);
if (current) {
const width = current.clientWidth;
const padding =
(isSmallScreen ? bodyPaddingSmallScreen : bodyPadding) - 5;
useEffect(() => {
if (gridRef.current && scrollTop && !scrollRestored.current) {
gridRef.current.scrollToPosition({ scrollLeft: 0, scrollTop });
scrollRestored.current = true;
}
}, [scrollTop]);
useEffect(() => {
if (
gridRef.current &&
scrollIndex != null &&
scrollIndex !== previousScrollIndex
) {
gridRef.current.scrollToCell({
rowIndex: scrollIndex,
columnIndex: 0,
setSize({
width: width - padding * 2,
height: windowHeight,
});
}
}, [scrollIndex, previousScrollIndex]);
}, [isSmallScreen, windowWidth, windowHeight, scrollerRef, bounds]);
useEffect(() => {
const currentScrollerRef = scrollerRef.current as HTMLElement;
const currentScrollListener = isSmallScreen ? window : currentScrollerRef;
const handleScroll = throttle(() => {
const { offsetTop = 0 } = currentScrollerRef;
const scrollTop =
(isSmallScreen
? getWindowScrollTopPosition()
: currentScrollerRef.scrollTop) - offsetTop;
listRef.current?.scrollTo(scrollTop);
}, 10);
currentScrollListener.addEventListener('scroll', handleScroll);
return () => {
handleScroll.cancel();
if (currentScrollListener) {
currentScrollListener.removeEventListener('scroll', handleScroll);
}
};
}, [isSmallScreen, listRef, scrollerRef]);
return (
<WindowScroller scrollElement={isSmallScreen ? undefined : scroller}>
{({ height, registerChild, onChildScroll, scrollTop }) => {
if (!height) {
return null;
}
return (
<div ref={measureRef}>
<Scroller
className={className}
scrollDirection={scrollDirections.HORIZONTAL}
>
{header}
{/* @ts-expect-error - ref type is incompatible */}
<div ref={registerChild}>
<Grid
{...otherProps}
ref={gridRef}
autoContainerWidth={true}
autoHeight={true}
autoWidth={true}
width={width}
height={height}
headerHeight={height - headerHeight}
rowHeight={rowHeight}
rowCount={items.length}
columnCount={1}
columnWidth={width}
scrollTop={scrollTop}
overscanRowCount={2}
cellRenderer={rowRenderer}
overscanIndicesGetter={overscanIndicesGetter}
scrollToAlignment="start"
isScrollingOptout={true}
className={styles.tableBodyContainer}
style={gridStyle}
containerStyle={containerStyle}
onScroll={onChildScroll}
/>
</div>
</Scroller>
</div>
);
}}
</WindowScroller>
<div ref={measureRef}>
<Scroller className={styles.tableScroller} scrollDirection="horizontal">
{Header}
<FixedSizeList<T>
ref={listRef}
style={{
width: '100%',
height: '100%',
overflow: 'none',
}}
width={size.width}
height={size.height}
itemCount={itemCount}
itemSize={rowHeight}
itemData={itemData}
overscanCount={20}
>
{Row}
</FixedSizeList>
</Scroller>
</div>
);
}