diff --git a/front/public/translations/en.json b/front/public/translations/en.json index 90399a83..92224374 100644 --- a/front/public/translations/en.json +++ b/front/public/translations/en.json @@ -95,7 +95,8 @@ "desc": "decs" }, "switchToGrid": "Switch to grid view", - "switchToList": "Switch to list view" + "switchToList": "Switch to list view", + "not": "Not" }, "profile": { "history": "History", diff --git a/front/src/ui/browse/header.tsx b/front/src/ui/browse/header.tsx index 8c246512..83062dd6 100644 --- a/front/src/ui/browse/header.tsx +++ b/front/src/ui/browse/header.tsx @@ -1,7 +1,7 @@ import ArrowDownward from "@material-symbols/svg-400/rounded/arrow_downward.svg"; import ArrowUpward from "@material-symbols/svg-400/rounded/arrow_upward.svg"; import Check from "@material-symbols/svg-400/rounded/check.svg"; -import Close from "@material-symbols/svg-400/rounded/close.svg"; +import CloseIcon from "@material-symbols/svg-400/rounded/close.svg"; import Collection from "@material-symbols/svg-400/rounded/collections_bookmark.svg"; import FilterList from "@material-symbols/svg-400/rounded/filter_list.svg"; import GridView from "@material-symbols/svg-400/rounded/grid_view.svg"; @@ -106,6 +106,30 @@ const FilterTrigger = ({ ); }; +const FilterChip = ({ + label, + onRemove, +}: { + label: string; + onRemove: () => void; +}) => { + return ( + +

{label}

+ +
+ ); +}; + const parseFilterValues = (filter: string, pattern: RegExp) => Array.from(filter.matchAll(pattern)).map((x) => x[1]); @@ -176,161 +200,223 @@ export const BrowseSettings = ({ }; return ( - - - - {Object.keys(MediaTypeIcons).map((x) => ( - applyFilters({ kind: x })} - /> - ))} - - ( - - )} - > - {Genre.options.map((genre) => { - const isIncluded = includedGenres.includes(genre); - const isExcluded = excludedGenres.includes(genre); - return ( + + + + + {Object.keys(MediaTypeIcons).map((x) => ( - {(isIncluded || isExcluded) && ( - - )} - - } - closeOnSelect={false} - onSelect={() => { - let nextIncluded = includedGenres; - let nextExcluded = excludedGenres; - if (isIncluded) { - // include -> exclude - nextIncluded = nextIncluded.filter((g) => g !== genre); - nextExcluded = [...nextExcluded, genre]; - } else if (isExcluded) { - // exclude -> neutral - nextExcluded = nextExcluded.filter((g) => g !== genre); - } else { - // neutral -> include - nextIncluded = [...nextIncluded, genre]; - } - applyFilters({ - nextIncludedGenres: nextIncluded, - nextExcludedGenres: nextExcluded, - }); - }} + key={x} + label={t( + `browse.mediatypekey.${x as keyof typeof MediaTypeIcons}`, + )} + selected={mediaType === x} + icon={MediaTypeIcons[x as keyof typeof MediaTypeIcons]} + onSelect={() => applyFilters({ kind: x })} /> - ); - })} - - ( - - )} - query={(search) => ({ - path: ["api", "studios"], - parser: Studio, - infinite: true, - params: { - query: search, - }, - })} - values={studioSlugs.map((x) => ({ slug: x, name: x }))} - getKey={(studio) => studio.slug} - getLabel={(studio) => studio.name} - onValueChange={(items) => - applyFilters({ nextStudios: items.map((item) => item.slug) }) - } - /> - ( - - )} - query={(search) => ({ - path: ["api", "staff"], - parser: Staff, - infinite: true, - params: { - query: search, - }, - })} - values={staffSlugs.map((x) => ({ slug: x, name: x }))} - getKey={(member) => member.slug} - getLabel={(member) => member.name} - onValueChange={(items) => - applyFilters({ nextStaff: items.map((item) => item.slug) }) - } - /> + ))} + + ( + + )} + > + {Genre.options.map((genre) => { + const isIncluded = includedGenres.includes(genre); + const isExcluded = excludedGenres.includes(genre); + return ( + + {(isIncluded || isExcluded) && ( + + )} + + } + closeOnSelect={false} + onSelect={() => { + let nextIncluded = includedGenres; + let nextExcluded = excludedGenres; + if (isIncluded) { + // include -> exclude + nextIncluded = nextIncluded.filter((g) => g !== genre); + nextExcluded = [...nextExcluded, genre]; + } else if (isExcluded) { + // exclude -> neutral + nextExcluded = nextExcluded.filter((g) => g !== genre); + } else { + // neutral -> include + nextIncluded = [...nextIncluded, genre]; + } + applyFilters({ + nextIncludedGenres: nextIncluded, + nextExcludedGenres: nextExcluded, + }); + }} + /> + ); + })} + + ( + + )} + query={(search) => ({ + path: ["api", "studios"], + parser: Studio, + infinite: true, + params: { + query: search, + }, + })} + values={studioSlugs.map((x) => ({ slug: x, name: x }))} + getKey={(studio) => studio.slug} + getLabel={(studio) => studio.name} + onValueChange={(items) => + applyFilters({ nextStudios: items.map((item) => item.slug) }) + } + /> + ( + + )} + query={(search) => ({ + path: ["api", "staff"], + parser: Staff, + infinite: true, + params: { + query: search, + }, + })} + values={staffSlugs.map((x) => ({ slug: x, name: x }))} + getKey={(member) => member.slug} + getLabel={(member) => member.name} + onValueChange={(items) => + applyFilters({ nextStaff: items.map((item) => item.slug) }) + } + /> + + + + {availableSorts.map((x) => ( + + setSort(x, sortBy === x && sortOrd === "asc" ? "desc" : "asc") + } + /> + ))} + +
+ setLayout("grid")} + className="m-1" + iconClassName={cn( + layout === "grid" && "fill-accent dark:fill-accent", + )} + {...tooltip(t("browse.switchToGrid"))} + /> + setLayout("list")} + className="m-1" + iconClassName={cn( + layout === "list" && "fill-accent dark:fill-accent", + )} + {...tooltip(t("browse.switchToList"))} + /> +
- - - {availableSorts.map((x) => ( - - setSort(x, sortBy === x && sortOrd === "asc" ? "desc" : "asc") + {(mediaType !== "all" || + includedGenres.length > 0 || + excludedGenres.length > 0 || + studioSlugs.length > 0 || + staffSlugs.length > 0) && ( + + {mediaType !== "all" && ( + applyFilters({ kind: "all" })} + /> + )} + {includedGenres.map((genre) => ( + + applyFilters({ + nextIncludedGenres: includedGenres.filter((g) => g !== genre), + }) } /> ))} - -
- setLayout("grid")} - className="m-1" - iconClassName={cn( - layout === "grid" && "fill-accent dark:fill-accent", - )} - {...tooltip(t("browse.switchToGrid"))} - /> - setLayout("list")} - className="m-1" - iconClassName={cn( - layout === "list" && "fill-accent dark:fill-accent", - )} - {...tooltip(t("browse.switchToList"))} - /> -
+ {excludedGenres.map((genre) => ( + + applyFilters({ + nextExcludedGenres: excludedGenres.filter((g) => g !== genre), + }) + } + /> + ))} + {studioSlugs.map((studio) => ( + + applyFilters({ + nextStudios: studioSlugs.filter((s) => s !== studio), + }) + } + /> + ))} + {staffSlugs.map((staff) => ( + + applyFilters({ + nextStaff: staffSlugs.filter((s) => s !== staff), + }) + } + /> + ))} + + )} ); };