group date pills when days are consecutive

This commit is contained in:
2024-07-14 01:25:44 +02:00
parent fcbd74ed34
commit 4f9d2e2bcf
2 changed files with 76 additions and 11 deletions

View File

@ -8,9 +8,14 @@ import {
EventFragment, EventFragment,
getFutureOccurrences, getFutureOccurrences,
} from "@/lib/event"; } from "@/lib/event";
import { formatDate, formatExtendedDateTime } from "@/lib/date"; import {
formatDate,
formatDateRange,
formatExtendedDateTime,
groupConsecutiveDates,
} from "@/lib/date";
const DATES_TO_SHOW = 2; const DATE_PILLS_TO_SHOW = 2;
export const EventItem = ({ export const EventItem = ({
event, event,
@ -22,8 +27,11 @@ export const EventItem = ({
size?: "small" | "medium" | "large"; size?: "small" | "medium" | "large";
}) => { }) => {
const futureOccurrences = getFutureOccurrences(event); const futureOccurrences = getFutureOccurrences(event);
const groupedOccurrences = groupConsecutiveDates(
futureOccurrences.map((occurrence) => occurrence.start)
);
const numOccurrences = event?.occurrences?.length ?? 0; const numOccurrences = event?.occurrences?.length ?? 0;
const nextOccurrence = numOccurrences ? futureOccurrences[0] : null; const nextOccurrence = numOccurrences ? groupedOccurrences[0] : null;
const featuredImage: any = event.featuredImage; const featuredImage: any = event.featuredImage;
return ( return (
@ -46,15 +54,15 @@ export const EventItem = ({
<div className={styles.time}> <div className={styles.time}>
{mode === "list" && nextOccurrence && ( {mode === "list" && nextOccurrence && (
<div className={styles.dates}> <div className={styles.dates}>
{futureOccurrences.slice(0, DATES_TO_SHOW).map((occurrence) => ( {groupedOccurrences.slice(0, DATE_PILLS_TO_SHOW).map((group) => (
<span key={occurrence.id} className={styles.datePill}> <span key={group[0]} className={styles.datePill}>
{formatDate(occurrence.start, "d. MMMM")} {formatDateRange(group)}
</span> </span>
))} ))}
{futureOccurrences.length > DATES_TO_SHOW && ( {groupedOccurrences.length > DATE_PILLS_TO_SHOW && (
<span className={styles.moreDates}>{`+${ <span className={styles.moreDates}>{`+${groupedOccurrences
futureOccurrences.length - DATES_TO_SHOW .slice(DATE_PILLS_TO_SHOW)
}`}</span> .reduce((sum, group) => sum + group.length, 0)}`}</span>
)} )}
</div> </div>
)} )}

View File

@ -1,4 +1,11 @@
import { isToday, isAfter, parse, compareAsc } from "date-fns"; import {
isToday,
isAfter,
parse,
compareAsc,
addDays,
isSameDay,
} from "date-fns";
import { nb } from "date-fns/locale"; import { nb } from "date-fns/locale";
import { toZonedTime, format } from "date-fns-tz"; import { toZonedTime, format } from "date-fns-tz";
@ -51,3 +58,53 @@ export function isTodayOrFuture(
export function compareDates(a: Date | string, b: Date | string) { export function compareDates(a: Date | string, b: Date | string) {
return compareAsc(new Date(a), new Date(b)); return compareAsc(new Date(a), new Date(b));
} }
export function isConsectutiveDays(a: Date, b: Date): boolean {
const nextDay = addDays(a, 1);
return isSameDay(nextDay, b);
}
export function groupConsecutiveDates(dates: string[]): string[][] {
const groupedDates: string[][] = [];
let group: string[] = [];
for (let i = 0; i < dates.length; i++) {
if (
i === 0 ||
isConsectutiveDays(toLocalTime(dates[i - 1]), toLocalTime(dates[i]))
) {
group.push(dates[i]);
} else {
groupedDates.push(group);
group = [dates[i]];
}
}
if (group.length > 0) {
groupedDates.push(group);
}
return groupedDates;
}
export function formatDateRange(dates: string[]): string {
if (dates.length === 1) {
return formatDate(dates[0], "d. MMMM");
}
const firstDate = toLocalTime(dates[0]);
const lastDate = toLocalTime(dates[dates.length - 1]);
if (firstDate.getMonth() === lastDate.getMonth()) {
// same month
return `${firstDate.getDay()}.-${lastDate.getDay()}. ${formatDate(
firstDate,
"MMMM"
)}`;
}
// continues into next month
const formattedFirstDate = formatDate(firstDate, "d. MMMM");
const formattedLastDate = formatDate(lastDate, "d. MMMM");
return `${formattedFirstDate} - ${formattedLastDate}`;
}