From c883fc66e63547e356b40aed5b394dd332442b78 Mon Sep 17 00:00:00 2001 From: SuhravHussen Date: Tue, 10 Dec 2024 14:34:34 +0600 Subject: [PATCH] fix: handle error when weightedDates is an empty array feat: add option to highlight or skip highlighting today's date --- components/ui/calendar-heatmap.tsx | 99 ++++++++++++++++-------------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/components/ui/calendar-heatmap.tsx b/components/ui/calendar-heatmap.tsx index 62ba032..f7e5cad 100644 --- a/components/ui/calendar-heatmap.tsx +++ b/components/ui/calendar-heatmap.tsx @@ -1,86 +1,92 @@ -"use client" +"use client"; -import * as React from "react" -import { ChevronLeft, ChevronRight } from "lucide-react" -import { DayPicker } from "react-day-picker" +import * as React from "react"; +import { ChevronLeft, ChevronRight } from "lucide-react"; +import { DayPicker } from "react-day-picker"; -import { cn } from "@/lib/utils" -import { buttonVariants } from "@/components/ui/button" +import { cn } from "@/lib/utils"; +import { buttonVariants } from "@/components/ui/button"; // type utilities -type UnionKeys = T extends T ? keyof T : never -type Expand = T extends T ? { [K in keyof T]: T[K] } : never +type UnionKeys = T extends T ? keyof T : never; +type Expand = T extends T ? { [K in keyof T]: T[K] } : never; type OneOf = { [K in keyof T]: Expand< T[K] & Partial, keyof T[K]>, never>> - > -}[number] + >; +}[number]; // types -export type Classname = string +export type Classname = string; export type WeightedDateEntry = { - date: Date - weight: number -} + date: Date; + weight: number; +}; interface IDatesPerVariant { - datesPerVariant: Date[][] + datesPerVariant: Date[][]; } interface IWeightedDatesEntry { - weightedDates: WeightedDateEntry[] + weightedDates: WeightedDateEntry[]; } -type VariantDatesInput = OneOf<[IDatesPerVariant, IWeightedDatesEntry]> +type VariantDatesInput = OneOf<[IDatesPerVariant, IWeightedDatesEntry]>; export type CalendarProps = React.ComponentProps & { - variantClassnames: Classname[] -} & VariantDatesInput + variantClassnames: Classname[]; + showToday?: boolean; +} & VariantDatesInput; /// utlity functions function useModifers( variantClassnames: Classname[], datesPerVariant: Date[][] ): [Record, Record] { - const noOfVariants = variantClassnames.length + const noOfVariants = variantClassnames.length; const variantLabels = [...Array(noOfVariants)].map( (_, idx) => `__variant${idx}` - ) + ); const modifiers = variantLabels.reduce((acc, key, index) => { - acc[key] = datesPerVariant[index] - return acc - }, {} as Record) + acc[key] = datesPerVariant[index]; + return acc; + }, {} as Record); const modifiersClassNames = variantLabels.reduce((acc, key, index) => { - acc[key] = variantClassnames[index] - return acc - }, {} as Record) + acc[key] = variantClassnames[index]; + return acc; + }, {} as Record); - return [modifiers, modifiersClassNames] + return [modifiers, modifiersClassNames]; } function categorizeDatesPerVariant( weightedDates: WeightedDateEntry[], noOfVariants: number ) { - const sortedEntries = weightedDates.sort((a, b) => a.weight - b.weight) + // Return array of empty arrays if weightedDates is empty + if (!weightedDates.length) { + return [...Array(noOfVariants)].map(() => [] as Date[]); + } - const categorizedRecord = [...Array(noOfVariants)].map(() => [] as Date[]) + const sortedEntries = weightedDates.sort((a, b) => a.weight - b.weight); + const categorizedRecord = [...Array(noOfVariants)].map(() => [] as Date[]); - const minNumber = sortedEntries[0].weight - const maxNumber = sortedEntries[sortedEntries.length - 1].weight - const range = minNumber == maxNumber ? 1 : (maxNumber - minNumber) / noOfVariants; + const minNumber = sortedEntries[0].weight; + const maxNumber = sortedEntries[sortedEntries.length - 1].weight; + const range = + minNumber === maxNumber ? 1 : (maxNumber - minNumber) / noOfVariants; sortedEntries.forEach((entry) => { const category = Math.min( Math.floor((entry.weight - minNumber) / range), noOfVariants - 1 - ) - categorizedRecord[category].push(entry.date) - }) + ); + categorizedRecord[category].push(entry.date); + }); - return categorizedRecord + return categorizedRecord; } function CalendarHeatmap({ @@ -90,18 +96,19 @@ function CalendarHeatmap({ className, classNames, showOutsideDays = true, + showToday = false, ...props }: CalendarProps) { - const noOfVariants = variantClassnames.length + const noOfVariants = variantClassnames.length; - weightedDates = weightedDates ?? [] + weightedDates = weightedDates ?? []; datesPerVariant = - datesPerVariant ?? categorizeDatesPerVariant(weightedDates, noOfVariants) + datesPerVariant ?? categorizeDatesPerVariant(weightedDates, noOfVariants); const [modifiers, modifiersClassNames] = useModifers( variantClassnames, datesPerVariant - ) + ); return ( - ) + ); } -CalendarHeatmap.displayName = "CalendarHeatmap" +CalendarHeatmap.displayName = "CalendarHeatmap"; -export { CalendarHeatmap } +export { CalendarHeatmap };