Fixed: Mobile add indexer modal layout (#2464)

* New: Add Indexer Filters are Collapsible

fixes #2431

* Fixed: Rename onToggleFilters to handleToggleFilters

* fix css lint
This commit is contained in:
bakerboy448 2025-08-17 21:46:59 -05:00 committed by Qstick
parent 04a6bba76b
commit 4350b6ce70
3 changed files with 80 additions and 11 deletions

View file

@ -33,6 +33,7 @@
.scroller { .scroller {
flex: 1 1 auto; flex: 1 1 auto;
min-height: 400px;
} }
.filterRow { .filterRow {
@ -57,29 +58,68 @@
font-weight: bold; font-weight: bold;
} }
.filtersToggle {
display: none;
align-items: center;
margin-bottom: 10px;
padding: 8px 12px;
border: 1px solid var(--borderColor);
border-radius: 4px;
background: transparent;
color: var(--textColor);
font-size: 14px;
cursor: pointer;
gap: 8px;
}
.filtersToggle:hover {
background-color: var(--hoverBackgroundColor);
}
.filterRowCollapsed {
display: none !important;
}
@media only screen and (max-width: $breakpointSmall) { @media only screen and (max-width: $breakpointSmall) {
.filterInput { .filterInput {
margin-bottom: 5px; margin-bottom: 8px;
} }
.alert { .notice {
display: none; display: none;
} }
.filtersToggle {
display: flex;
}
.filterRow { .filterRow {
display: block; display: block;
margin-bottom: 10px; margin-bottom: 10px;
padding: 10px;
border: 1px solid var(--borderColor);
border-radius: 4px;
background-color: var(--cardBackgroundColor);
} }
.filterContainer { .filterContainer {
margin-right: 0; margin-right: 0;
margin-bottom: 5px; margin-bottom: 8px;
}
.filterContainer:last-child {
margin-bottom: 0;
} }
.scroller { .scroller {
margin-right: -30px; margin-right: -15px;
margin-bottom: -30px; margin-bottom: -15px;
margin-left: -30px; margin-left: -15px;
min-height: 300px;
}
.modalBody {
padding: 15px;
} }
} }

View file

@ -7,6 +7,8 @@ interface CssExports {
'filterInput': string; 'filterInput': string;
'filterLabel': string; 'filterLabel': string;
'filterRow': string; 'filterRow': string;
'filterRowCollapsed': string;
'filtersToggle': string;
'indexers': string; 'indexers': string;
'modalBody': string; 'modalBody': string;
'modalFooter': string; 'modalFooter': string;

View file

@ -1,3 +1,4 @@
import classNames from 'classnames';
import { some } from 'lodash'; import { some } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
@ -7,6 +8,7 @@ import Alert from 'Components/Alert';
import EnhancedSelectInput from 'Components/Form/EnhancedSelectInput'; import EnhancedSelectInput from 'Components/Form/EnhancedSelectInput';
import NewznabCategorySelectInputConnector from 'Components/Form/NewznabCategorySelectInputConnector'; import NewznabCategorySelectInputConnector from 'Components/Form/NewznabCategorySelectInputConnector';
import TextInput from 'Components/Form/TextInput'; import TextInput from 'Components/Form/TextInput';
import Icon from 'Components/Icon';
import Button from 'Components/Link/Button'; import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import ModalBody from 'Components/Modal/ModalBody'; import ModalBody from 'Components/Modal/ModalBody';
@ -16,7 +18,7 @@ import ModalHeader from 'Components/Modal/ModalHeader';
import Scroller from 'Components/Scroller/Scroller'; import Scroller from 'Components/Scroller/Scroller';
import Table from 'Components/Table/Table'; import Table from 'Components/Table/Table';
import TableBody from 'Components/Table/TableBody'; import TableBody from 'Components/Table/TableBody';
import { kinds, scrollDirections } from 'Helpers/Props'; import { icons, kinds, scrollDirections } from 'Helpers/Props';
import Indexer, { IndexerCategory } from 'Indexer/Indexer'; import Indexer, { IndexerCategory } from 'Indexer/Indexer';
import { import {
fetchIndexerSchema, fetchIndexerSchema,
@ -25,6 +27,7 @@ import {
} from 'Store/Actions/indexerActions'; } from 'Store/Actions/indexerActions';
import createAllIndexersSelector from 'Store/Selectors/createAllIndexersSelector'; import createAllIndexersSelector from 'Store/Selectors/createAllIndexersSelector';
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector'; import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import { SortCallback } from 'typings/callbacks'; import { SortCallback } from 'typings/callbacks';
import sortByProp from 'Utilities/Array/sortByProp'; import sortByProp from 'Utilities/Array/sortByProp';
import getErrorMessage from 'Utilities/Object/getErrorMessage'; import getErrorMessage from 'Utilities/Object/getErrorMessage';
@ -111,7 +114,8 @@ function createAddIndexersSelector() {
return createSelector( return createSelector(
createClientSideCollectionSelector('indexers.schema'), createClientSideCollectionSelector('indexers.schema'),
createAllIndexersSelector(), createAllIndexersSelector(),
(indexers: IndexerAppState, allIndexers) => { createDimensionsSelector(),
(indexers: IndexerAppState, allIndexers, dimensions) => {
const { isFetching, isPopulated, error, items, sortDirection, sortKey } = const { isFetching, isPopulated, error, items, sortDirection, sortKey } =
indexers; indexers;
@ -130,6 +134,7 @@ function createAddIndexersSelector() {
indexers: indexerList, indexers: indexerList,
sortKey, sortKey,
sortDirection, sortDirection,
isSmallScreen: dimensions.isSmallScreen,
}; };
} }
); );
@ -143,8 +148,15 @@ interface AddIndexerModalContentProps {
function AddIndexerModalContent(props: AddIndexerModalContentProps) { function AddIndexerModalContent(props: AddIndexerModalContentProps) {
const { onSelectIndexer, onModalClose } = props; const { onSelectIndexer, onModalClose } = props;
const { isFetching, isPopulated, error, indexers, sortKey, sortDirection } = const {
useSelector(createAddIndexersSelector()); isFetching,
isPopulated,
error,
indexers,
sortKey,
sortDirection,
isSmallScreen,
} = useSelector(createAddIndexersSelector());
const dispatch = useDispatch(); const dispatch = useDispatch();
const [filter, setFilter] = useState(''); const [filter, setFilter] = useState('');
@ -152,6 +164,7 @@ function AddIndexerModalContent(props: AddIndexerModalContentProps) {
const [filterLanguages, setFilterLanguages] = useState<string[]>([]); const [filterLanguages, setFilterLanguages] = useState<string[]>([]);
const [filterPrivacyLevels, setFilterPrivacyLevels] = useState<string[]>([]); const [filterPrivacyLevels, setFilterPrivacyLevels] = useState<string[]>([]);
const [filterCategories, setFilterCategories] = useState<number[]>([]); const [filterCategories, setFilterCategories] = useState<number[]>([]);
const [isFiltersCollapsed, setIsFiltersCollapsed] = useState(isSmallScreen);
useEffect( useEffect(
() => { () => {
@ -196,6 +209,10 @@ function AddIndexerModalContent(props: AddIndexerModalContentProps) {
[setFilterCategories] [setFilterCategories]
); );
const handleToggleFilters = useCallback(() => {
setIsFiltersCollapsed(!isFiltersCollapsed);
}, [isFiltersCollapsed]);
const onIndexerSelect = useCallback( const onIndexerSelect = useCallback(
({ ({
implementation, implementation,
@ -322,7 +339,17 @@ function AddIndexerModalContent(props: AddIndexerModalContentProps) {
onChange={onFilterChange} onChange={onFilterChange}
/> />
<div className={styles.filterRow}> <Button className={styles.filtersToggle} onPress={handleToggleFilters}>
<Icon name={isFiltersCollapsed ? icons.EXPAND : icons.COLLAPSE} />
{translate('Filters')}
</Button>
<div
className={classNames(
styles.filterRow,
isFiltersCollapsed && styles.filterRowCollapsed
)}
>
<div className={styles.filterContainer}> <div className={styles.filterContainer}>
<label className={styles.filterLabel}> <label className={styles.filterLabel}>
{translate('Protocol')} {translate('Protocol')}