import { throttle } from 'lodash'; import React, { useCallback, useEffect, useMemo, useRef, useState, } from 'react'; import { useSelector } from 'react-redux'; import { FixedSizeList as List, ListChildComponentProps } from 'react-window'; import TextInput from 'Components/Form/TextInput'; import Button from 'Components/Link/Button'; import ModalBody from 'Components/Modal/ModalBody'; import ModalContent from 'Components/Modal/ModalContent'; import ModalFooter from 'Components/Modal/ModalFooter'; import ModalHeader from 'Components/Modal/ModalHeader'; import Scroller from 'Components/Scroller/Scroller'; import Column from 'Components/Table/Column'; import VirtualTableRowButton from 'Components/Table/VirtualTableRowButton'; import { scrollDirections } from 'Helpers/Props'; import Movie from 'Movie/Movie'; import createAllMoviesSelector from 'Store/Selectors/createAllMoviesSelector'; import dimensions from 'Styles/Variables/dimensions'; import translate from 'Utilities/String/translate'; import SelectMovieModalTableHeader from './SelectMovieModalTableHeader'; import SelectMovieRow from './SelectMovieRow'; import styles from './SelectMovieModalContent.css'; const columns = [ { name: 'title', get label() { return translate('Title'); }, isVisible: true, }, { name: 'year', get label() { return translate('Year'); }, isVisible: true, }, { name: 'imdbId', get label() { return translate('ImdbId'); }, isVisible: true, }, { name: 'tmdbId', get label() { return translate('TmdbId'); }, isVisible: true, }, ]; const bodyPadding = parseInt(dimensions.pageContentBodyPadding); interface SelectMovieModalContentProps { modalTitle: string; onMovieSelect(movie: Movie): void; onModalClose(): void; } interface RowItemData { items: Movie[]; columns: Column[]; onMovieSelect(movieId: number): void; } const Row: React.FC> = ({ index, style, data, }) => { const { items, columns, onMovieSelect } = data; if (index >= items.length) { return null; } const movie = items[index]; return ( onMovieSelect(movie.id)} > ); }; function SelectMovieModalContent(props: SelectMovieModalContentProps) { const { modalTitle, onMovieSelect, onModalClose } = props; const listRef = useRef>(null); const scrollerRef = useRef(null); const allMovies: Movie[] = useSelector(createAllMoviesSelector()); const [filter, setFilter] = useState(''); const [size, setSize] = useState({ width: 0, height: 0 }); const windowHeight = window.innerHeight; useEffect(() => { const current = scrollerRef?.current as HTMLElement; if (current) { const width = current.clientWidth; const height = current.clientHeight; const padding = bodyPadding - 5; setSize({ width: width - padding * 2, height: height + padding, }); } }, [windowHeight, scrollerRef]); useEffect(() => { const currentScrollerRef = scrollerRef.current as HTMLElement; const currentScrollListener = currentScrollerRef; const handleScroll = throttle(() => { const { offsetTop = 0 } = currentScrollerRef; const scrollTop = currentScrollerRef.scrollTop - offsetTop; listRef.current?.scrollTo(scrollTop); }, 10); currentScrollListener.addEventListener('scroll', handleScroll); return () => { handleScroll.cancel(); if (currentScrollListener) { currentScrollListener.removeEventListener('scroll', handleScroll); } }; }, [listRef, scrollerRef]); const onFilterChange = useCallback( ({ value }: { value: string }) => { setFilter(value); }, [setFilter] ); const onMovieSelectWrapper = useCallback( (movieId: number) => { const movie = allMovies.find((s) => s.id === movieId) as Movie; onMovieSelect(movie); }, [allMovies, onMovieSelect] ); const items = useMemo(() => { const sorted = [...allMovies].sort((a, b) => a.sortTitle.localeCompare(b.sortTitle) ); return sorted.filter((item) => item.title.toLowerCase().includes(filter.toLowerCase()) ); }, [allMovies, filter]); return ( {modalTitle} - Select Movie ref={listRef} style={{ width: '100%', height: '100%', overflow: 'none', }} width={size.width} height={size.height} itemCount={items.length} itemSize={38} itemData={{ items, columns, onMovieSelect: onMovieSelectWrapper, }} > {Row} ); } export default SelectMovieModalContent;