support filtering multiple categories
This commit is contained in:
@ -1,6 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { useQueryState, parseAsStringLiteral, parseAsString } from "nuqs";
|
||||
import {
|
||||
useQueryState,
|
||||
parseAsStringLiteral,
|
||||
parseAsString,
|
||||
parseAsArrayOf,
|
||||
} from "nuqs";
|
||||
import { EventItem } from "./EventItem";
|
||||
import { EventFilter, EventFilterExplained } from "./EventFilter";
|
||||
import {
|
||||
@ -44,12 +49,15 @@ export const EventContainer = ({
|
||||
"mode",
|
||||
parseAsStringLiteral(["list", "calendar"]).withDefault("list")
|
||||
);
|
||||
const [category, setCategory] = useQueryState("category", parseAsString);
|
||||
const [categories, setCategories] = useQueryState(
|
||||
"category",
|
||||
parseAsArrayOf(parseAsString, ",")
|
||||
);
|
||||
const [organizer, setOrganizer] = useQueryState("organizer", parseAsString);
|
||||
const [venue, setVenue] = useQueryState("venue", parseAsString);
|
||||
|
||||
const resetFilters = () => {
|
||||
setCategory(null);
|
||||
setCategories(null);
|
||||
setOrganizer(null);
|
||||
setVenue(null);
|
||||
};
|
||||
@ -105,10 +113,10 @@ export const EventContainer = ({
|
||||
)
|
||||
.filter(
|
||||
(x) =>
|
||||
!category ||
|
||||
!categories ||
|
||||
x.categories
|
||||
.map((eventCategory) => eventCategory.slug)
|
||||
.includes(category)
|
||||
.filter((x) => categories.includes(x)).length !== 0
|
||||
)
|
||||
.filter(
|
||||
(x) =>
|
||||
@ -147,7 +155,7 @@ export const EventContainer = ({
|
||||
</button>
|
||||
</div>
|
||||
<div className={styles.filterButtons}>
|
||||
{(category || organizer || venue) && (
|
||||
{(categories || organizer || venue) && (
|
||||
<button onClick={onResetFilters} className="button tertiary">
|
||||
<span>Vis alle</span>
|
||||
<Icon type="noFilter" />
|
||||
@ -161,8 +169,8 @@ export const EventContainer = ({
|
||||
</div>
|
||||
<EventFilter
|
||||
eventCategories={filterableCategories}
|
||||
setCategory={setCategory}
|
||||
activeCategory={category}
|
||||
setCategories={setCategories}
|
||||
activeCategories={categories}
|
||||
eventOrganizers={filterableOrganizers}
|
||||
setOrganizer={setOrganizer}
|
||||
activeOrganizer={organizer}
|
||||
@ -173,7 +181,7 @@ export const EventContainer = ({
|
||||
/>
|
||||
<EventFilterExplained
|
||||
eventCategories={filterableCategories}
|
||||
activeCategory={category}
|
||||
activeCategories={categories}
|
||||
eventOrganizers={filterableOrganizers}
|
||||
activeOrganizer={organizer}
|
||||
venues={filterableVenues}
|
||||
@ -188,9 +196,7 @@ export const EventContainer = ({
|
||||
|
||||
const EventList = ({ events }: { events: EventFragment[] }) => {
|
||||
if (events.length === 0) {
|
||||
return (
|
||||
<div className={styles.noEvents}>Ingen kommende arrangementer.</div>
|
||||
);
|
||||
return <div className={styles.noEvents}>Ingen kommende arrangementer.</div>;
|
||||
}
|
||||
return (
|
||||
<ul className={styles.eventList}>
|
||||
|
@ -2,12 +2,12 @@ import { EventCategory, EventOrganizer } from "@/lib/event";
|
||||
|
||||
import styles from "./eventFilter.module.scss";
|
||||
import { VenueFragment } from "@/gql/graphql";
|
||||
import { Icon } from "../general/Icon";
|
||||
import { formatHumanReadableList } from "@/lib/common";
|
||||
|
||||
export const EventFilter = ({
|
||||
eventCategories,
|
||||
setCategory,
|
||||
activeCategory,
|
||||
setCategories,
|
||||
activeCategories,
|
||||
eventOrganizers,
|
||||
setOrganizer,
|
||||
activeOrganizer,
|
||||
@ -17,8 +17,8 @@ export const EventFilter = ({
|
||||
isVisible,
|
||||
}: {
|
||||
eventCategories: EventCategory[];
|
||||
setCategory: (slug: string | null) => void;
|
||||
activeCategory: string | null;
|
||||
setCategories: (slug: string[] | null) => void;
|
||||
activeCategories: string[] | null;
|
||||
eventOrganizers: EventOrganizer[];
|
||||
setOrganizer: (slug: string | null) => void;
|
||||
activeOrganizer: string | null;
|
||||
@ -35,6 +35,12 @@ export const EventFilter = ({
|
||||
const venue = e.target.value;
|
||||
setVenue(venue === "" ? null : venue);
|
||||
};
|
||||
const toggleCategory = (category: string) => {
|
||||
const newCategories = activeCategories?.includes(category)
|
||||
? activeCategories?.filter((x) => x !== category)
|
||||
: [...(activeCategories ?? []), category];
|
||||
setCategories(newCategories.length === 0 ? null : newCategories);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.eventFilter} data-show={isVisible}>
|
||||
@ -48,12 +54,8 @@ export const EventFilter = ({
|
||||
<li key={category.slug}>
|
||||
<button
|
||||
className="button toggler"
|
||||
data-active={activeCategory === category.slug}
|
||||
onClick={() =>
|
||||
setCategory(
|
||||
activeCategory === category.slug ? null : category.slug
|
||||
)
|
||||
}
|
||||
data-active={activeCategories?.includes(category.slug)}
|
||||
onClick={() => toggleCategory(category.slug)}
|
||||
>
|
||||
{category.name}
|
||||
</button>
|
||||
@ -102,34 +104,38 @@ export const EventFilter = ({
|
||||
|
||||
export const EventFilterExplained = ({
|
||||
eventCategories,
|
||||
activeCategory,
|
||||
activeCategories,
|
||||
eventOrganizers,
|
||||
activeOrganizer,
|
||||
venues,
|
||||
activeVenue,
|
||||
}: {
|
||||
eventCategories: EventCategory[];
|
||||
activeCategory: string | null;
|
||||
activeCategories: string[] | null;
|
||||
eventOrganizers: EventOrganizer[];
|
||||
activeOrganizer: string | null;
|
||||
venues: VenueFragment[];
|
||||
activeVenue: string | null;
|
||||
}) => {
|
||||
const category =
|
||||
activeCategory && eventCategories.find((x) => x.slug === activeCategory);
|
||||
const categories = eventCategories.filter((x) =>
|
||||
activeCategories?.includes(x.slug)
|
||||
);
|
||||
const organizer =
|
||||
activeOrganizer && eventOrganizers.find((x) => x.slug === activeOrganizer);
|
||||
const venue = activeVenue && venues.find((x) => x.slug === activeVenue);
|
||||
|
||||
const categoryList = formatHumanReadableList(categories.map((x) => x.name));
|
||||
const hasCategories = categoryList.length !== 0;
|
||||
|
||||
let text = "";
|
||||
if (category && organizer && venue) {
|
||||
text = `${category.name} av ${organizer.name} ${venue.preposition} ${venue.title}`;
|
||||
} else if (category && organizer) {
|
||||
text = `${category.name} av ${organizer.name}`;
|
||||
} else if (category && venue) {
|
||||
text = `${category.name} ${venue.preposition} ${venue.title}`;
|
||||
} else if (category) {
|
||||
text = `${category.name}`;
|
||||
if (hasCategories && organizer && venue) {
|
||||
text = `${categoryList} av ${organizer.name} ${venue.preposition} ${venue.title}`;
|
||||
} else if (hasCategories && organizer) {
|
||||
text = `${categoryList} av ${organizer.name}`;
|
||||
} else if (hasCategories && venue) {
|
||||
text = `${categoryList} ${venue.preposition} ${venue.title}`;
|
||||
} else if (hasCategories) {
|
||||
text = `${categoryList}`;
|
||||
} else if (organizer && venue) {
|
||||
text = `Arrangeres av ${organizer.name} ${venue.preposition} ${venue.title}`;
|
||||
} else if (organizer) {
|
||||
|
@ -76,6 +76,21 @@ export function formatNorwegianPhoneNumber(phone: string): string {
|
||||
return phone;
|
||||
}
|
||||
|
||||
export function formatHumanReadableList(array: (string | number)[]): string {
|
||||
const length = array.length;
|
||||
|
||||
if (length === 0) {
|
||||
return "";
|
||||
}
|
||||
if (length === 1) {
|
||||
return array[0].toString();
|
||||
}
|
||||
if (length === 2) {
|
||||
return array.join(" og ");
|
||||
}
|
||||
return array.slice(0, -1).join(", ") + " og " + array[length - 1];
|
||||
}
|
||||
|
||||
const OneLevelOfBlocksFragmentDefinition = graphql(`
|
||||
fragment OneLevelOfBlocks on StreamFieldInterface {
|
||||
id
|
||||
|
Reference in New Issue
Block a user