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 (
-
-
-
-
- (
-
- )}
- 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),
+ })
+ }
+ />
+ ))}
+
+ )}
);
};