Skip to content

Commit 1cafe1c

Browse files
authored
Merge pull request #7130 from jellyfin/resetFilters
Add a Reset Filters button to bottom of filter dropdown
2 parents b9829c1 + 6c4cb68 commit 1cafe1c

7 files changed

Lines changed: 108 additions & 3 deletions

File tree

src/apps/experimental/components/library/filter/FilterButton.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-ite
22
import React, { FC, useCallback } from 'react';
33
import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp';
44
import FilterAlt from '@mui/icons-material/FilterAlt';
5+
import Clear from '@mui/icons-material/Clear';
6+
57
import Button from '@mui/material/Button';
68
import Popover from '@mui/material/Popover';
79
import MuiAccordion, { AccordionProps } from '@mui/material/Accordion';
@@ -110,6 +112,16 @@ const FilterButton: FC<FilterButtonProps> = ({
110112
setAnchorEl(event.currentTarget);
111113
}, []);
112114

115+
const handleResetFiltersClick = useCallback(() => {
116+
if (hasFilters) {
117+
setLibraryViewSettings((prevState) => ({
118+
...prevState,
119+
StartIndex: 0,
120+
Filters: {}
121+
}));
122+
}
123+
}, [hasFilters, setLibraryViewSettings]);
124+
113125
const handleClose = useCallback(() => {
114126
setAnchorEl(null);
115127
}, []);
@@ -447,6 +459,19 @@ const FilterButton: FC<FilterButtonProps> = ({
447459
</AccordionDetails>
448460
</Accordion>
449461
)}
462+
<Button
463+
disabled={!hasFilters}
464+
title={globalize.translate('ResetFilters')}
465+
aria-describedby={id}
466+
onClick={handleResetFiltersClick}
467+
fullWidth={true}
468+
startIcon={<Clear />}
469+
sx={{
470+
justifyContent: 'right'
471+
}}
472+
>
473+
{globalize.translate('ResetFilters')}
474+
</Button>
450475
</Popover>
451476
</>
452477
);

src/components/filterdialog/filterIndicator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import './filterIndicator.scss';
22

3-
function getFilterStatus(query) {
3+
export function getFilterStatus(query) {
44
return Boolean(
55
query.Filters
66
|| query.IsFavorite

src/components/filterdialog/filterIndicator.scss

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,21 @@
1919
background: #03a9f4;
2020
font-weight: bold;
2121
}
22+
23+
.resetFilters {
24+
align-items: center;
25+
display: flex;
26+
font-size: 86%;
27+
justify-content: right;
28+
margin: 0;
29+
width: 100%;
30+
}
31+
32+
.resetFilters:disabled {
33+
color: #777;
34+
cursor: default;
35+
}
36+
37+
.resetFilters .material-icons {
38+
margin-right: 3px;
39+
}

src/components/filterdialog/filterdialog.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import '../../elements/emby-collapse/emby-collapse';
99
import './style.scss';
1010
import template from './filterdialog.template.html';
1111
import { stopMultiSelect } from '../../components/multiSelect/multiSelect';
12+
import { getFilterStatus } from 'components/filterdialog/filterIndicator';
1213

1314
function merge(resultItems, queryItems, delimiter) {
1415
if (!queryItems) {
@@ -115,6 +116,14 @@ function updateFilterControls(context, options) {
115116
*/
116117
function triggerChange(instance) {
117118
stopMultiSelect();
119+
120+
const hasFilters = getFilterStatus(instance.options.query);
121+
if (hasFilters) {
122+
enableByClass(document, 'resetFilters');
123+
} else {
124+
disableByClass(document, 'resetFilters');
125+
}
126+
118127
Events.trigger(instance, 'filterchange');
119128
}
120129

@@ -145,6 +154,22 @@ function setVisibility(context, options) {
145154
if (options.mode === 'episodes') {
146155
showByClass(context, 'episodeFilter');
147156
}
157+
158+
if (!options.hasFilters) {
159+
disableByClass(context, 'resetFilters');
160+
}
161+
}
162+
163+
function enableByClass(context, className) {
164+
for (const elem of context.querySelectorAll(`.${className}`)) {
165+
elem.disabled = false;
166+
}
167+
}
168+
169+
function disableByClass(context, className) {
170+
for (const elem of context.querySelectorAll(`.${className}`)) {
171+
elem.disabled = true;
172+
}
148173
}
149174

150175
function showByClass(context, className) {
@@ -254,6 +279,16 @@ class FilterDialog {
254279
for (const elem of context.querySelectorAll('.chkVideoTypeFilter')) {
255280
elem.addEventListener('change', () => this.onVideoTypeFilterChange(elem));
256281
}
282+
283+
const resetFilters = context.querySelector('.resetFilters');
284+
resetFilters.addEventListener('click', () => {
285+
for (const elem of context.querySelectorAll('.filterDialogContent input[type="checkbox"]:checked')) {
286+
elem.checked = false;
287+
}
288+
this.resetQuery(query);
289+
triggerChange(this);
290+
});
291+
257292
const chk3DFilter = context.querySelector('.chk3DFilter');
258293
chk3DFilter.addEventListener('change', () => {
259294
query.StartIndex = 0;
@@ -416,6 +451,26 @@ class FilterDialog {
416451
});
417452
}
418453

454+
resetQuery(query) {
455+
query.IsFavorite = null;
456+
query.IsHD = null;
457+
query.Is3D = null;
458+
query.Is4K = null;
459+
query.Filters = '';
460+
query.SeriesStatus = '';
461+
query.OfficialRatings = '';
462+
query.Genres = '';
463+
query.VideoTypes = '';
464+
query.StartIndex = 0;
465+
query.HasSpecialFeature = null;
466+
query.HasSubtitles = null;
467+
query.HasThemeSong = null;
468+
query.HasThemeVideo = null;
469+
query.HasTrailer = null;
470+
query.Tags = null;
471+
query.Years = '';
472+
}
473+
419474
show() {
420475
return new Promise((resolve) => {
421476
const dlg = dialogHelper.createDialog({

src/components/filterdialog/filterdialog.template.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,9 @@
140140
<div class="collapseContent filterOptions">
141141
</div>
142142
</div>
143+
144+
<button is="emby-button" type="submit" class="resetFilters raised">
145+
<span class="material-icons clear" aria-hidden="true"></span>
146+
<span>${ResetFilters}</span>
147+
</button>
143148
</div>

src/controllers/movies/movies.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import cardBuilder from '../../components/cardbuilder/cardBuilder';
77
import globalize from '../../lib/globalize';
88
import Events from '../../utils/events.ts';
99
import { playbackManager } from '../../components/playback/playbackmanager';
10-
import { setFilterStatus } from 'components/filterdialog/filterIndicator';
10+
import { getFilterStatus, setFilterStatus } from 'components/filterdialog/filterIndicator';
1111

1212
import '../../elements/emby-itemscontainer/emby-itemscontainer';
1313

@@ -311,7 +311,8 @@ export default function (view, params, tabContent, options) {
311311
const filterDialog = new FilterDialog({
312312
query: query,
313313
mode: 'movies',
314-
serverId: ApiClient.serverId()
314+
serverId: ApiClient.serverId(),
315+
hasFilters: getFilterStatus(query)
315316
});
316317
Events.on(filterDialog, 'filterchange', () => {
317318
query.StartIndex = 0;

src/strings/en-us.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,6 +1504,7 @@
15041504
"Retry": "Retry",
15051505
"RetryWithGlobalSearch": "Retry with a global search",
15061506
"Reset": "Reset",
1507+
"ResetFilters": "Reset filters",
15071508
"ResetPassword": "Reset Password",
15081509
"ResolutionMatchSource": "Match Source",
15091510
"Restart": "Restart",

0 commit comments

Comments
 (0)