Compare commits

...

2 Commits

Author SHA1 Message Date
1hitsong
c01e1a22d3 Remove trailing extra empty line 2025-09-09 15:22:39 -04:00
1hitsong
c342e38858 Add a Reset Filters button to bottom of filter dropdown
- If no filters are applied, disable button
- Apply to both standard and experimental layouts
2025-09-09 13:45:28 -04:00
7 changed files with 108 additions and 3 deletions

View File

@@ -2,6 +2,8 @@ import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-ite
import React, { FC, useCallback } from 'react';
import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp';
import FilterAlt from '@mui/icons-material/FilterAlt';
import Clear from '@mui/icons-material/Clear';
import Button from '@mui/material/Button';
import Popover from '@mui/material/Popover';
import MuiAccordion, { AccordionProps } from '@mui/material/Accordion';
@@ -110,6 +112,16 @@ const FilterButton: FC<FilterButtonProps> = ({
setAnchorEl(event.currentTarget);
}, []);
const handleResetFiltersClick = useCallback(() => {
if (hasFilters) {
setLibraryViewSettings((prevState) => ({
...prevState,
StartIndex: 0,
Filters: {}
}));
}
}, [hasFilters, setLibraryViewSettings]);
const handleClose = useCallback(() => {
setAnchorEl(null);
}, []);
@@ -447,6 +459,19 @@ const FilterButton: FC<FilterButtonProps> = ({
</AccordionDetails>
</Accordion>
)}
<Button
disabled={!hasFilters}
title={globalize.translate('ResetFilters')}
aria-describedby={id}
onClick={handleResetFiltersClick}
fullWidth={true}
startIcon={<Clear />}
sx={{
justifyContent: 'right'
}}
>
{globalize.translate('ResetFilters')}
</Button>
</Popover>
</>
);

View File

@@ -1,6 +1,6 @@
import './filterIndicator.scss';
function getFilterStatus(query) {
export function getFilterStatus(query) {
return Boolean(
query.Filters
|| query.IsFavorite

View File

@@ -19,3 +19,21 @@
background: #03a9f4;
font-weight: bold;
}
.resetFilters {
align-items: center;
display: flex;
font-size: 86%;
justify-content: right;
margin: 0;
width: 100%;
}
.resetFilters:disabled {
color: #777;
cursor: default;
}
.resetFilters .material-icons {
margin-right: 3px;
}

View File

@@ -9,6 +9,7 @@ import '../../elements/emby-collapse/emby-collapse';
import './style.scss';
import template from './filterdialog.template.html';
import { stopMultiSelect } from '../../components/multiSelect/multiSelect';
import { getFilterStatus } from 'components/filterdialog/filterIndicator';
function merge(resultItems, queryItems, delimiter) {
if (!queryItems) {
@@ -115,6 +116,14 @@ function updateFilterControls(context, options) {
*/
function triggerChange(instance) {
stopMultiSelect();
const hasFilters = getFilterStatus(instance.options.query);
if (hasFilters) {
enableByClass(document, 'resetFilters');
} else {
disableByClass(document, 'resetFilters');
}
Events.trigger(instance, 'filterchange');
}
@@ -145,6 +154,22 @@ function setVisibility(context, options) {
if (options.mode === 'episodes') {
showByClass(context, 'episodeFilter');
}
if (!options.hasFilters) {
disableByClass(context, 'resetFilters');
}
}
function enableByClass(context, className) {
for (const elem of context.querySelectorAll(`.${className}`)) {
elem.disabled = false;
}
}
function disableByClass(context, className) {
for (const elem of context.querySelectorAll(`.${className}`)) {
elem.disabled = true;
}
}
function showByClass(context, className) {
@@ -254,6 +279,16 @@ class FilterDialog {
for (const elem of context.querySelectorAll('.chkVideoTypeFilter')) {
elem.addEventListener('change', () => this.onVideoTypeFilterChange(elem));
}
const resetFilters = context.querySelector('.resetFilters');
resetFilters.addEventListener('click', () => {
for (const elem of context.querySelectorAll('.filterDialogContent input[type="checkbox"]:checked')) {
elem.checked = false;
}
this.resetQuery(query);
triggerChange(this);
});
const chk3DFilter = context.querySelector('.chk3DFilter');
chk3DFilter.addEventListener('change', () => {
query.StartIndex = 0;
@@ -416,6 +451,26 @@ class FilterDialog {
});
}
resetQuery(query) {
query.IsFavorite = null;
query.IsHD = null;
query.Is3D = null;
query.Is4K = null;
query.Filters = '';
query.SeriesStatus = '';
query.OfficialRatings = '';
query.Genres = '';
query.VideoTypes = '';
query.StartIndex = 0;
query.HasSpecialFeature = null;
query.HasSubtitles = null;
query.HasThemeSong = null;
query.HasThemeVideo = null;
query.HasTrailer = null;
query.Tags = null;
query.Years = '';
}
show() {
return new Promise((resolve) => {
const dlg = dialogHelper.createDialog({

View File

@@ -140,4 +140,9 @@
<div class="collapseContent filterOptions">
</div>
</div>
<button is="emby-button" type="submit" class="resetFilters raised">
<span class="material-icons clear" aria-hidden="true"></span>
<span>${ResetFilters}</span>
</button>
</div>

View File

@@ -7,7 +7,7 @@ import cardBuilder from '../../components/cardbuilder/cardBuilder';
import globalize from '../../lib/globalize';
import Events from '../../utils/events.ts';
import { playbackManager } from '../../components/playback/playbackmanager';
import { setFilterStatus } from 'components/filterdialog/filterIndicator';
import { getFilterStatus, setFilterStatus } from 'components/filterdialog/filterIndicator';
import '../../elements/emby-itemscontainer/emby-itemscontainer';
@@ -301,7 +301,8 @@ export default function (view, params, tabContent, options) {
const filterDialog = new FilterDialog({
query: query,
mode: 'movies',
serverId: ApiClient.serverId()
serverId: ApiClient.serverId(),
hasFilters: getFilterStatus(query)
});
Events.on(filterDialog, 'filterchange', () => {
query.StartIndex = 0;

View File

@@ -1491,6 +1491,7 @@
"Retry": "Retry",
"RetryWithGlobalSearch": "Retry with a global search",
"Reset": "Reset",
"ResetFilters": "Reset filters",
"ResetPassword": "Reset Password",
"ResolutionMatchSource": "Match Source",
"Restart": "Restart",