UNPKG

@ilamy/calendar

Version:

A full-featured React calendar component library built with Shadcn-Ui, Tailwind CSS, and TypeScript.

1,321 lines (1,303 loc) 581 kB
"use client"; // src/features/month-view/components/view/month-view.tsx import { AnimatePresence as AnimatePresence3, motion as motion3 } from "motion/react"; import React5, { useMemo as useMemo2 } from "react"; // src/contexts/calendar-context/context.ts import { createContext, useContext } from "react"; var CalendarContext = createContext(undefined); var useCalendarContext = () => { const context = useContext(CalendarContext); if (context === undefined) { throw new Error("useCalendarContext must be used within a CalendarProvider"); } return context; }; var useIlamyCalendarContext = () => { const context = useContext(CalendarContext); if (context === undefined) { throw new Error("useIlamyCalendarContext must be used within a CalendarProvider"); } return { currentDate: context.currentDate, view: context.view, events: context.events, isEventFormOpen: context.isEventFormOpen, selectedEvent: context.selectedEvent, selectedDate: context.selectedDate, firstDayOfWeek: context.firstDayOfWeek, setCurrentDate: context.setCurrentDate, selectDate: context.selectDate, setView: context.setView, nextPeriod: context.nextPeriod, prevPeriod: context.prevPeriod, today: context.today, addEvent: context.addEvent, updateEvent: context.updateEvent, deleteEvent: context.deleteEvent, openEventForm: context.openEventForm, closeEventForm: context.closeEventForm }; }; // src/components/ui/button.tsx import { Slot } from "@radix-ui/react-slot"; import { cva } from "class-variance-authority"; // src/lib/utils.ts import { clsx } from "clsx"; // src/lib/dayjs-config.ts import dayjs from "dayjs"; import weekday from "dayjs/plugin/weekday.js"; import weekOfYear from "dayjs/plugin/weekOfYear.js"; import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js"; import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js"; import isBetween from "dayjs/plugin/isBetween.js"; import minMax from "dayjs/plugin/minMax.js"; import timezone from "dayjs/plugin/timezone.js"; import utc from "dayjs/plugin/utc.js"; import localeData from "dayjs/plugin/localeData.js"; import"dayjs/locale/af.js"; import"dayjs/locale/am.js"; import"dayjs/locale/ar-dz.js"; import"dayjs/locale/ar-iq.js"; import"dayjs/locale/ar-kw.js"; import"dayjs/locale/ar-ly.js"; import"dayjs/locale/ar-ma.js"; import"dayjs/locale/ar-sa.js"; import"dayjs/locale/ar-tn.js"; import"dayjs/locale/ar.js"; import"dayjs/locale/az.js"; import"dayjs/locale/be.js"; import"dayjs/locale/bg.js"; import"dayjs/locale/bi.js"; import"dayjs/locale/bm.js"; import"dayjs/locale/bn-bd.js"; import"dayjs/locale/bn.js"; import"dayjs/locale/bo.js"; import"dayjs/locale/br.js"; import"dayjs/locale/bs.js"; import"dayjs/locale/ca.js"; import"dayjs/locale/cs.js"; import"dayjs/locale/cv.js"; import"dayjs/locale/cy.js"; import"dayjs/locale/da.js"; import"dayjs/locale/de-at.js"; import"dayjs/locale/de-ch.js"; import"dayjs/locale/de.js"; import"dayjs/locale/dv.js"; import"dayjs/locale/el.js"; import"dayjs/locale/en-au.js"; import"dayjs/locale/en-ca.js"; import"dayjs/locale/en-gb.js"; import"dayjs/locale/en-ie.js"; import"dayjs/locale/en-il.js"; import"dayjs/locale/en-in.js"; import"dayjs/locale/en-nz.js"; import"dayjs/locale/en-sg.js"; import"dayjs/locale/en-tt.js"; import"dayjs/locale/en.js"; import"dayjs/locale/eo.js"; import"dayjs/locale/es-do.js"; import"dayjs/locale/es-mx.js"; import"dayjs/locale/es-pr.js"; import"dayjs/locale/es-us.js"; import"dayjs/locale/es.js"; import"dayjs/locale/et.js"; import"dayjs/locale/eu.js"; import"dayjs/locale/fa.js"; import"dayjs/locale/fi.js"; import"dayjs/locale/fo.js"; import"dayjs/locale/fr-ca.js"; import"dayjs/locale/fr-ch.js"; import"dayjs/locale/fr.js"; import"dayjs/locale/fy.js"; import"dayjs/locale/ga.js"; import"dayjs/locale/gd.js"; import"dayjs/locale/gl.js"; import"dayjs/locale/gom-latn.js"; import"dayjs/locale/gu.js"; import"dayjs/locale/he.js"; import"dayjs/locale/hi.js"; import"dayjs/locale/hr.js"; import"dayjs/locale/ht.js"; import"dayjs/locale/hu.js"; import"dayjs/locale/hy-am.js"; import"dayjs/locale/id.js"; import"dayjs/locale/is.js"; import"dayjs/locale/it-ch.js"; import"dayjs/locale/it.js"; import"dayjs/locale/ja.js"; import"dayjs/locale/jv.js"; import"dayjs/locale/ka.js"; import"dayjs/locale/kk.js"; import"dayjs/locale/km.js"; import"dayjs/locale/kn.js"; import"dayjs/locale/ko.js"; import"dayjs/locale/ku.js"; import"dayjs/locale/ky.js"; import"dayjs/locale/lb.js"; import"dayjs/locale/lo.js"; import"dayjs/locale/lt.js"; import"dayjs/locale/lv.js"; import"dayjs/locale/me.js"; import"dayjs/locale/mi.js"; import"dayjs/locale/mk.js"; import"dayjs/locale/ml.js"; import"dayjs/locale/mn.js"; import"dayjs/locale/mr.js"; import"dayjs/locale/ms-my.js"; import"dayjs/locale/ms.js"; import"dayjs/locale/mt.js"; import"dayjs/locale/my.js"; import"dayjs/locale/nb.js"; import"dayjs/locale/ne.js"; import"dayjs/locale/nl-be.js"; import"dayjs/locale/nl.js"; import"dayjs/locale/nn.js"; import"dayjs/locale/oc-lnc.js"; import"dayjs/locale/pa-in.js"; import"dayjs/locale/pl.js"; import"dayjs/locale/pt-br.js"; import"dayjs/locale/pt.js"; import"dayjs/locale/rn.js"; import"dayjs/locale/ro.js"; import"dayjs/locale/ru.js"; import"dayjs/locale/rw.js"; import"dayjs/locale/sd.js"; import"dayjs/locale/se.js"; import"dayjs/locale/si.js"; import"dayjs/locale/sk.js"; import"dayjs/locale/sl.js"; import"dayjs/locale/sq.js"; import"dayjs/locale/sr-cyrl.js"; import"dayjs/locale/sr.js"; import"dayjs/locale/ss.js"; import"dayjs/locale/sv-fi.js"; import"dayjs/locale/sv.js"; import"dayjs/locale/sw.js"; import"dayjs/locale/ta.js"; import"dayjs/locale/te.js"; import"dayjs/locale/tet.js"; import"dayjs/locale/tg.js"; import"dayjs/locale/th.js"; import"dayjs/locale/tk.js"; import"dayjs/locale/tl-ph.js"; import"dayjs/locale/tlh.js"; import"dayjs/locale/tr.js"; import"dayjs/locale/tzl.js"; import"dayjs/locale/tzm-latn.js"; import"dayjs/locale/tzm.js"; import"dayjs/locale/ug-cn.js"; import"dayjs/locale/uk.js"; import"dayjs/locale/ur.js"; import"dayjs/locale/uz-latn.js"; import"dayjs/locale/uz.js"; import"dayjs/locale/vi.js"; import"dayjs/locale/x-pseudo.js"; import"dayjs/locale/yo.js"; import"dayjs/locale/zh-cn.js"; import"dayjs/locale/zh-hk.js"; import"dayjs/locale/zh-tw.js"; import"dayjs/locale/zh.js"; dayjs.extend(weekday); dayjs.extend(weekOfYear); dayjs.extend(isSameOrAfter); dayjs.extend(isSameOrBefore); dayjs.extend(isBetween); dayjs.extend(minMax); dayjs.extend(timezone); dayjs.extend(utc); dayjs.extend(localeData); var dayjs_config_default = dayjs; // src/lib/utils.ts import { twMerge } from "tailwind-merge"; function cn(...inputs) { return twMerge(clsx(inputs)); } function safeDate(date) { if (dayjs_config_default.isDayjs(date)) { return date; } const parsedDate = dayjs_config_default(date); return parsedDate.isValid() ? parsedDate : dayjs_config_default(); } var omitKeys = (obj, keys) => { const result = { ...obj }; for (const key of keys) { delete result[key]; } return result; }; function normalizePublicFacingCalendarEvent(events) { if (!events || !events.length) { return []; } return events.map((event) => { return { ...event, start: dayjs_config_default.isDayjs(event.start) ? event.start : dayjs_config_default(event.start), end: dayjs_config_default.isDayjs(event.end) ? event.end : dayjs_config_default(event.end) }; }); } // src/components/ui/button.tsx import { jsx } from "react/jsx-runtime"; var buttonVariants = cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", { variants: { variant: { default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", link: "text-primary underline-offset-4 hover:underline" }, size: { default: "h-9 px-4 py-2 has-[>svg]:px-3", sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", lg: "h-10 rounded-md px-6 has-[>svg]:px-4", icon: "size-9" } }, defaultVariants: { variant: "default", size: "default" } }); function Button({ className, variant, size, asChild = false, ...props }) { const Comp = asChild ? Slot : "button"; return /* @__PURE__ */ jsx(Comp, { "data-slot": "button", className: cn(buttonVariants({ variant, size, className })), ...props }); } // src/components/ui/calendar.tsx import * as React from "react"; import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; import { DayPicker, getDefaultClassNames } from "react-day-picker"; import { jsx as jsx2 } from "react/jsx-runtime"; function Calendar({ className, classNames, showOutsideDays = true, captionLayout = "label", buttonVariant = "ghost", formatters, components, ...props }) { const defaultClassNames = getDefaultClassNames(); return /* @__PURE__ */ jsx2(DayPicker, { showOutsideDays, className: cn("bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent", String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className), captionLayout, formatters: { formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }), ...formatters }, classNames: { root: cn("w-fit", defaultClassNames.root), months: cn("flex gap-4 flex-col md:flex-row relative", defaultClassNames.months), month: cn("flex flex-col w-full gap-4", defaultClassNames.month), nav: cn("flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between", defaultClassNames.nav), button_previous: cn(buttonVariants({ variant: buttonVariant }), "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none", defaultClassNames.button_previous), button_next: cn(buttonVariants({ variant: buttonVariant }), "size-(--cell-size) aria-disabled:opacity-50 p-0 select-none", defaultClassNames.button_next), month_caption: cn("flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)", defaultClassNames.month_caption), dropdowns: cn("w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5", defaultClassNames.dropdowns), dropdown_root: cn("relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md", defaultClassNames.dropdown_root), dropdown: cn("absolute bg-popover inset-0 opacity-0", defaultClassNames.dropdown), caption_label: cn("select-none font-medium", captionLayout === "label" ? "text-sm" : "rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5", defaultClassNames.caption_label), table: "w-full border-collapse", weekdays: cn("flex", defaultClassNames.weekdays), weekday: cn("text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none", defaultClassNames.weekday), week: cn("flex w-full mt-2", defaultClassNames.week), week_number_header: cn("select-none w-(--cell-size)", defaultClassNames.week_number_header), week_number: cn("text-[0.8rem] select-none text-muted-foreground", defaultClassNames.week_number), day: cn("relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none", defaultClassNames.day), range_start: cn("rounded-l-md bg-accent", defaultClassNames.range_start), range_middle: cn("rounded-none", defaultClassNames.range_middle), range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end), today: cn("bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none", defaultClassNames.today), outside: cn("text-muted-foreground aria-selected:text-muted-foreground", defaultClassNames.outside), disabled: cn("text-muted-foreground opacity-50", defaultClassNames.disabled), hidden: cn("invisible", defaultClassNames.hidden), ...classNames }, components: { Root: ({ className: className2, rootRef, ...props2 }) => { return /* @__PURE__ */ jsx2("div", { "data-slot": "calendar", ref: rootRef, className: cn(className2), ...props2 }); }, Chevron: ({ className: className2, orientation, ...props2 }) => { if (orientation === "left") { return /* @__PURE__ */ jsx2(ChevronLeftIcon, { className: cn("size-4", className2), ...props2 }); } if (orientation === "right") { return /* @__PURE__ */ jsx2(ChevronRightIcon, { className: cn("size-4", className2), ...props2 }); } return /* @__PURE__ */ jsx2(ChevronDownIcon, { className: cn("size-4", className2), ...props2 }); }, DayButton: CalendarDayButton, WeekNumber: ({ children, ...props2 }) => { return /* @__PURE__ */ jsx2("td", { ...props2, children: /* @__PURE__ */ jsx2("div", { className: "flex size-(--cell-size) items-center justify-center text-center", children }) }); }, ...components }, ...props }); } function CalendarDayButton({ className, day, modifiers, ...props }) { const defaultClassNames = getDefaultClassNames(); const ref = React.useRef(null); React.useEffect(() => { if (modifiers.focused) { ref.current?.focus(); } }, [modifiers.focused]); return /* @__PURE__ */ jsx2(Button, { ref, variant: "ghost", size: "icon", "data-day": day.date.toLocaleDateString(), "data-selected-single": modifiers.selected && !modifiers.range_start && !modifiers.range_end && !modifiers.range_middle, "data-range-start": modifiers.range_start, "data-range-end": modifiers.range_end, "data-range-middle": modifiers.range_middle, className: cn("data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70", defaultClassNames.day, className), ...props }); } // src/components/ui/checkbox.tsx import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; import { CheckIcon } from "lucide-react"; import { jsx as jsx3 } from "react/jsx-runtime"; function Checkbox({ className, ...props }) { return /* @__PURE__ */ jsx3(CheckboxPrimitive.Root, { "data-slot": "checkbox", className: cn("peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", className), ...props, children: /* @__PURE__ */ jsx3(CheckboxPrimitive.Indicator, { "data-slot": "checkbox-indicator", className: "flex items-center justify-center text-current transition-none", children: /* @__PURE__ */ jsx3(CheckIcon, { className: "size-3.5" }) }) }); } // src/components/ui/card.tsx import { jsx as jsx4 } from "react/jsx-runtime"; function Card({ className, ...props }) { return /* @__PURE__ */ jsx4("div", { "data-slot": "card", className: cn("text-card-foreground flex flex-col gap-6 rounded-xl border shadow-sm", className), ...props }); } function CardHeader({ className, ...props }) { return /* @__PURE__ */ jsx4("div", { "data-slot": "card-header", className: cn("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 p-4 pb-0 has-data-[slot=card-action]:grid-cols-[1fr_auto]", className), ...props }); } function CardTitle({ className, ...props }) { return /* @__PURE__ */ jsx4("div", { "data-slot": "card-title", className: cn("leading-none font-semibold", className), ...props }); } function CardContent({ className, ...props }) { return /* @__PURE__ */ jsx4("div", { "data-slot": "card-content", className: cn("p-4 pt-0", className), ...props }); } // src/components/ui/date-picker.tsx import { Calendar as CalendarIcon } from "lucide-react"; // src/components/ui/popover.tsx import * as PopoverPrimitive from "@radix-ui/react-popover"; import { jsx as jsx5 } from "react/jsx-runtime"; function Popover({ ...props }) { return /* @__PURE__ */ jsx5(PopoverPrimitive.Root, { "data-slot": "popover", ...props }); } function PopoverTrigger({ ...props }) { return /* @__PURE__ */ jsx5(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props }); } function PopoverContent({ className, align = "center", sideOffset = 4, ...props }) { return /* @__PURE__ */ jsx5(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx5(PopoverPrimitive.Content, { "data-slot": "popover-content", align, sideOffset, className: cn("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden", className), ...props }) }); } // src/components/ui/date-picker.tsx import { useRef as useRef2 } from "react"; import { PopoverClose } from "@radix-ui/react-popover"; import { jsx as jsx6, jsxs } from "react/jsx-runtime"; function DatePicker({ date, closeOnSelect, setDate, label = "Pick a date", className }) { const popOverRef = useRef2(null); const onSelect = (date2) => { setDate(date2); if (closeOnSelect) { popOverRef.current?.click(); } }; return /* @__PURE__ */ jsx6("div", { className, children: /* @__PURE__ */ jsxs(Popover, { children: [ /* @__PURE__ */ jsx6(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", className: cn("w-full justify-start text-left font-normal", !date && "text-muted-foreground"), children: [ /* @__PURE__ */ jsx6(CalendarIcon, { className: "mr-2 h-4 w-4" }), date ? dayjs_config_default(date).format("MMM D, YYYY") : /* @__PURE__ */ jsx6("span", { children: label }) ] }) }), /* @__PURE__ */ jsxs(PopoverContent, { className: "w-auto p-0", align: "start", children: [ /* @__PURE__ */ jsx6(PopoverClose, { ref: popOverRef }), /* @__PURE__ */ jsx6(Calendar, { mode: "single", selected: date, onSelect, month: date, initialFocus: true }) ] }) ] }) }); } // src/components/ui/dialog.tsx import * as DialogPrimitive from "@radix-ui/react-dialog"; import { XIcon } from "lucide-react"; import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime"; function Dialog({ ...props }) { return /* @__PURE__ */ jsx7(DialogPrimitive.Root, { "data-slot": "dialog", ...props }); } function DialogPortal({ ...props }) { return /* @__PURE__ */ jsx7(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props }); } function DialogOverlay({ className, ...props }) { return /* @__PURE__ */ jsx7(DialogPrimitive.Overlay, { "data-slot": "dialog-overlay", className: cn("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", className), ...props }); } function DialogContent({ className, children, ...props }) { return /* @__PURE__ */ jsxs2(DialogPortal, { "data-slot": "dialog-portal", children: [ /* @__PURE__ */ jsx7(DialogOverlay, {}), /* @__PURE__ */ jsxs2(DialogPrimitive.Content, { "data-slot": "dialog-content", className: cn("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg max-h-[90%] overflow-y-scroll", className), ...props, children: [ children, /* @__PURE__ */ jsxs2(DialogPrimitive.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", children: [ /* @__PURE__ */ jsx7(XIcon, {}), /* @__PURE__ */ jsx7("span", { className: "sr-only", children: "Close" }) ] }) ] }) ] }); } function DialogHeader({ className, ...props }) { return /* @__PURE__ */ jsx7("div", { "data-slot": "dialog-header", className: cn("flex flex-col gap-2 text-center sm:text-left", className), ...props }); } function DialogFooter({ className, ...props }) { return /* @__PURE__ */ jsx7("div", { "data-slot": "dialog-footer", className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className), ...props }); } function DialogTitle({ className, ...props }) { return /* @__PURE__ */ jsx7(DialogPrimitive.Title, { "data-slot": "dialog-title", className: cn("text-lg leading-none font-semibold", className), ...props }); } function DialogDescription({ className, ...props }) { return /* @__PURE__ */ jsx7(DialogPrimitive.Description, { "data-slot": "dialog-description", className: cn("text-muted-foreground text-sm", className), ...props }); } // src/components/ui/input.tsx import { jsx as jsx8 } from "react/jsx-runtime"; function Input({ className, type, ...props }) { return /* @__PURE__ */ jsx8("input", { type, "data-slot": "input", className: cn("file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", className), ...props }); } // src/components/ui/label.tsx import * as LabelPrimitive from "@radix-ui/react-label"; import { jsx as jsx9 } from "react/jsx-runtime"; function Label({ className, ...props }) { return /* @__PURE__ */ jsx9(LabelPrimitive.Root, { "data-slot": "label", className: cn("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className), ...props }); } // src/components/ui/scroll-area.tsx import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"; import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime"; function ScrollArea({ className, children, ...props }) { return /* @__PURE__ */ jsxs3(ScrollAreaPrimitive.Root, { "data-slot": "scroll-area", className: cn("relative", className), ...props, children: [ /* @__PURE__ */ jsx10(ScrollAreaPrimitive.Viewport, { ...props.viewPortProps, "data-slot": "scroll-area-viewport", className: cn("focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1", props.viewPortProps?.className), children }), /* @__PURE__ */ jsx10(ScrollBar, {}), /* @__PURE__ */ jsx10(ScrollAreaPrimitive.Corner, {}) ] }); } function ScrollBar({ className, orientation = "vertical", ...props }) { return /* @__PURE__ */ jsx10(ScrollAreaPrimitive.ScrollAreaScrollbar, { "data-slot": "scroll-area-scrollbar", orientation, className: cn("flex touch-none p-px transition-colors select-none", orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent", orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent", className), ...props, children: /* @__PURE__ */ jsx10(ScrollAreaPrimitive.ScrollAreaThumb, { "data-slot": "scroll-area-thumb", className: "bg-border relative flex-1 rounded-full" }) }); } // src/components/ui/select.tsx import * as SelectPrimitive from "@radix-ui/react-select"; import { CheckIcon as CheckIcon2, ChevronDownIcon as ChevronDownIcon2, ChevronUpIcon } from "lucide-react"; import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime"; function Select({ ...props }) { return /* @__PURE__ */ jsx11(SelectPrimitive.Root, { "data-slot": "select", ...props }); } function SelectValue({ ...props }) { return /* @__PURE__ */ jsx11(SelectPrimitive.Value, { "data-slot": "select-value", ...props }); } function SelectTrigger({ className, size = "default", children, ...props }) { return /* @__PURE__ */ jsxs4(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn("border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props, children: [ children, /* @__PURE__ */ jsx11(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx11(ChevronDownIcon2, { className: "size-4 opacity-50" }) }) ] }); } function SelectContent({ className, children, position = "popper", ...props }) { return /* @__PURE__ */ jsx11(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs4(SelectPrimitive.Content, { "data-slot": "select-content", className: cn("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className), position, ...props, children: [ /* @__PURE__ */ jsx11(SelectScrollUpButton, {}), /* @__PURE__ */ jsx11(SelectPrimitive.Viewport, { className: cn("p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"), children }), /* @__PURE__ */ jsx11(SelectScrollDownButton, {}) ] }) }); } function SelectItem({ className, children, ...props }) { return /* @__PURE__ */ jsxs4(SelectPrimitive.Item, { "data-slot": "select-item", className: cn("focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className), ...props, children: [ /* @__PURE__ */ jsx11("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsx11(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx11(CheckIcon2, { className: "size-4" }) }) }), /* @__PURE__ */ jsx11(SelectPrimitive.ItemText, { children }) ] }); } function SelectScrollUpButton({ className, ...props }) { return /* @__PURE__ */ jsx11(SelectPrimitive.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: cn("flex cursor-default items-center justify-center py-1", className), ...props, children: /* @__PURE__ */ jsx11(ChevronUpIcon, { className: "size-4" }) }); } function SelectScrollDownButton({ className, ...props }) { return /* @__PURE__ */ jsx11(SelectPrimitive.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: cn("flex cursor-default items-center justify-center py-1", className), ...props, children: /* @__PURE__ */ jsx11(ChevronDownIcon2, { className: "size-4" }) }); } // src/components/ui/tabs.tsx import * as TabsPrimitive from "@radix-ui/react-tabs"; import { jsx as jsx12 } from "react/jsx-runtime"; // src/components/ui/tooltip.tsx import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime"; function TooltipProvider({ delayDuration = 0, ...props }) { return /* @__PURE__ */ jsx13(TooltipPrimitive.Provider, { "data-slot": "tooltip-provider", delayDuration, ...props }); } function Tooltip({ ...props }) { return /* @__PURE__ */ jsx13(TooltipProvider, { children: /* @__PURE__ */ jsx13(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props }) }); } function TooltipTrigger({ ...props }) { return /* @__PURE__ */ jsx13(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props }); } function TooltipContent({ className, sideOffset = 0, children, ...props }) { return /* @__PURE__ */ jsx13(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs5(TooltipPrimitive.Content, { "data-slot": "tooltip-content", sideOffset, className: cn("bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance", className), ...props, children: [ children, /* @__PURE__ */ jsx13(TooltipPrimitive.Arrow, { className: "bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" }) ] }) }); } // src/features/month-view/components/all-events-dialog.tsx import { useImperativeHandle, useState } from "react"; // src/features/draggable-event/draggable-event.tsx import { useDraggable } from "@dnd-kit/core"; import { AnimatePresence, motion } from "motion/react"; import { memo } from "react"; import { jsx as jsx14 } from "react/jsx-runtime"; function DraggableEventUnmemoized({ elementId, event, className, style, disableDrag = false }) { const { onEventClick, renderEvent, disableEventClick, disableDragAndDrop } = useCalendarContext(); const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ id: elementId, data: { event, type: "calendar-event" }, disabled: disableDrag || disableDragAndDrop }); const DefaultEventContent = () => /* @__PURE__ */ jsx14("div", { className: cn(event.backgroundColor || "bg-blue-500", event.color || "text-white", "h-full w-full px-1 border-[1.5px] border-card rounded-md text-left overflow-clip"), style: { backgroundColor: event.backgroundColor, color: event.color }, children: /* @__PURE__ */ jsx14("p", { className: "text-[10px] font-semibold sm:text-xs mt-0.5", children: event.title }) }); return /* @__PURE__ */ jsx14(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx14(motion.div, { ref: setNodeRef, ...attributes, ...listeners, initial: { opacity: 0, y: -50 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -50 }, layout: true, layoutId: elementId, transition: { duration: 0.4, ease: "easeInOut" }, className: cn("truncate h-full w-full", disableDrag || disableDragAndDrop ? disableEventClick ? "cursor-default" : "cursor-pointer" : "cursor-grab", isDragging && !(disableDrag || disableDragAndDrop) && "cursor-grabbing shadow-lg", className), style, onClick: (e) => { e.stopPropagation(); onEventClick(event); }, children: renderEvent ? renderEvent(event) : /* @__PURE__ */ jsx14(DefaultEventContent, {}) }, elementId) }); } var DraggableEvent = memo(DraggableEventUnmemoized, (prevProps, nextProps) => { return prevProps.elementId === nextProps.elementId && prevProps.disableDrag === nextProps.disableDrag && prevProps.className === nextProps.className && prevProps.event === nextProps.event; }); // src/features/month-view/components/all-events-dialog.tsx import { jsx as jsx15, jsxs as jsxs6 } from "react/jsx-runtime"; var AllEventDialog = ({ ref }) => { const [dialogOpen, setDialogOpen] = useState(false); const [selectedDayEvents, setSelectedDayEvents] = useState(null); const { currentDate, firstDayOfWeek } = useCalendarContext(); useImperativeHandle(ref, () => ({ open: () => setDialogOpen(true), close: () => setDialogOpen(false), setSelectedDayEvents: (dayEvents) => setSelectedDayEvents(dayEvents) })); const firstDayOfMonth = currentDate.startOf("month"); let adjustedFirstDayOfCalendar = firstDayOfMonth.clone(); while (adjustedFirstDayOfCalendar.day() !== firstDayOfWeek) { adjustedFirstDayOfCalendar = adjustedFirstDayOfCalendar.subtract(1, "day"); } return /* @__PURE__ */ jsx15(Dialog, { open: dialogOpen, onOpenChange: setDialogOpen, children: /* @__PURE__ */ jsxs6(DialogContent, { className: "max-h-[80vh] max-w-md overflow-y-auto", children: [ /* @__PURE__ */ jsx15(DialogHeader, { children: /* @__PURE__ */ jsx15(DialogTitle, { children: selectedDayEvents && selectedDayEvents.day.format("MMMM D, YYYY") }) }), /* @__PURE__ */ jsx15("div", { className: "mt-4 space-y-3", children: selectedDayEvents && selectedDayEvents.events.map((event) => { return /* @__PURE__ */ jsx15(DraggableEvent, { elementId: `all-events-dialog-event-$${event.id}`, event, className: "relative my-1 h-[30px]" }, event.id); }) }) ] }) }); }; // src/features/month-view/components/day-cell.tsx import React3 from "react"; // src/features/droppable-cell/droppable-cell.tsx import { useDroppable } from "@dnd-kit/core"; import { jsx as jsx16 } from "react/jsx-runtime"; function DroppableCell({ id, type, date, hour, minute, children, className, style, "data-testid": dataTestId }) { const { onCellClick, disableDragAndDrop, disableCellClick } = useCalendarContext(); const { isOver, setNodeRef } = useDroppable({ id, data: { type, date, hour, minute }, disabled: disableDragAndDrop }); const handleCellClick = (e) => { e.stopPropagation(); if (disableCellClick) { return; } const startDate = date.hour(hour ?? 0).minute(minute ?? 0); let endDate = startDate.clone(); if (hour !== undefined && minute !== undefined) { endDate = endDate.hour(hour).minute(minute + 15); } else if (hour !== undefined) { endDate = endDate.hour(hour + 1).minute(0); } else { endDate = endDate.hour(23).minute(59); } onCellClick(startDate, endDate); }; return /* @__PURE__ */ jsx16("div", { ref: setNodeRef, "data-testid": dataTestId, className: cn(className, isOver && !disableDragAndDrop && "bg-accent", disableCellClick ? "cursor-default" : "cursor-pointer"), onClick: handleCellClick, style, children }); } // src/features/month-view/components/day-cell.tsx import { jsx as jsx17, jsxs as jsxs7, Fragment } from "react/jsx-runtime"; var DayCell = ({ index, day, className = "" }) => { const allEventsDialogRef = React3.useRef(null); const { currentLocale, getEventsForDateRange, currentDate, firstDayOfWeek, dayMaxEvents = 0 } = useCalendarContext(); const todayEvents = getEventsForDateRange(day.startOf("day"), day.endOf("day")); const firstDayOfMonth = currentDate.startOf("month"); let adjustedFirstDayOfCalendar = firstDayOfMonth.clone(); while (adjustedFirstDayOfCalendar.day() !== firstDayOfWeek) { adjustedFirstDayOfCalendar = adjustedFirstDayOfCalendar.subtract(1, "day"); } const showAllEvents = (day2, events) => { allEventsDialogRef.current?.setSelectedDayEvents({ day: day2, events }); allEventsDialogRef.current?.open(); }; const isToday = day.isSame(dayjs_config_default(), "day"); const isCurrentMonth = day.month() === currentDate.month(); const isLastColumn = index === 6; const hiddenEventsCount = todayEvents.length - dayMaxEvents; const hasHiddenEvents = hiddenEventsCount > 0; return /* @__PURE__ */ jsxs7(Fragment, { children: [ /* @__PURE__ */ jsx17(DroppableCell, { id: `day-cell-${day.format("YYYY-MM-DD")}`, type: "day-cell", "data-testid": `day-cell-${day.format("YYYY-MM-DD")}`, date: day, className: cn("cursor-pointer overflow-clip p-1 hover:bg-accent min-h-[60px]", !isCurrentMonth && "bg-secondary text-muted-foreground", isLastColumn && "border-r-0", className), children: /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1", children: [ /* @__PURE__ */ jsx17("div", { className: cn("flex h-5 w-5 items-center justify-center rounded-full text-xs sm:h-6 sm:w-6", isToday && "bg-primary text-primary-foreground font-medium"), children: Intl.DateTimeFormat(currentLocale, { day: "numeric" }).format(day.toDate()) }), todayEvents.slice(0, dayMaxEvents).map((event, rowIndex) => /* @__PURE__ */ jsx17("div", { className: "h-[20px] w-full", "data-testid": event?.title }, `empty-${rowIndex}`)), hasHiddenEvents && /* @__PURE__ */ jsxs7("div", { className: "text-muted-foreground hover:text-foreground cursor-pointer text-[10px] whitespace-nowrap sm:text-xs mt-1", onClick: (e) => { e.stopPropagation(); showAllEvents(day, todayEvents); }, onKeyDown: (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); e.stopPropagation(); showAllEvents(day, todayEvents); } }, tabIndex: 0, role: "button", children: [ "+", hiddenEventsCount, " more" ] }) ] }) }), /* @__PURE__ */ jsx17(AllEventDialog, { ref: allEventsDialogRef }) ] }); }; // src/features/month-view/components/month-header/month-header.tsx import { AnimatePresence as AnimatePresence2, motion as motion2 } from "motion/react"; import { useMemo } from "react"; import { jsx as jsx18, jsxs as jsxs8 } from "react/jsx-runtime"; var MonthHeader = ({ className }) => { const { firstDayOfWeek, currentLocale, stickyViewHeader, viewHeaderClassName } = useCalendarContext(); const weekDays = useMemo(() => { const days = dayjs_config_default.weekdays().map((day) => day.toLowerCase()); const shortDays = dayjs_config_default.weekdaysShort().map((day) => day.toLowerCase()); for (let i = 0;i < firstDayOfWeek; i++) { const dayToMove = days.shift(); const shortDayToMove = shortDays.shift(); if (dayToMove) { days.push(dayToMove); } if (shortDayToMove) { shortDays.push(shortDayToMove); } } return { days, shortDays }; }, [firstDayOfWeek, currentLocale]); return /* @__PURE__ */ jsx18("div", { className: cn("grid grid-cols-7 border-b", stickyViewHeader && "sticky top-0 z-100", viewHeaderClassName, className), "data-testid": "month-header", children: weekDays.days.map((weekDay, index) => /* @__PURE__ */ jsx18(AnimatePresence2, { mode: "wait", children: /* @__PURE__ */ jsxs8(motion2.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, transition: { duration: 0.25, ease: "easeInOut", delay: index * 0.05 }, className: "py-2 text-center font-medium border-r first:border-l", "data-testid": `weekday-header-${weekDay}`, children: [ /* @__PURE__ */ jsx18("span", { className: "hidden text-sm sm:inline", children: weekDay }), /* @__PURE__ */ jsx18("span", { className: "text-xs sm:hidden", children: weekDays.shortDays[index] }) ] }, weekDay) }, weekDay)) }); }; // src/lib/constants.ts var GAP_BETWEEN_ELEMENTS = 1; var DAY_NUMBER_HEIGHT = 28; var EVENT_BAR_HEIGHT = 24; var WEEK_DAYS_NUMBER_MAP = { sunday: 0, monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6 }; // src/hooks/useProcessedWeekEvents.ts var useProcessedWeekEvents = ({ days }) => { const { getEventsForDateRange, dayMaxEvents } = useCalendarContext(); const weekStart = days[0].startOf("day"); const weekEnd = days[6].endOf("day"); const weekEvents = getEventsForDateRange(weekStart, weekEnd); const multiDayEvents = weekEvents.filter((e) => e.end.diff(e.start, "day") > 0); const singleDayEvents = weekEvents.filter((e) => e.end.diff(e.start, "day") === 0); const sortedMultiDay = [...multiDayEvents].sort((a, b) => { const startDiff = a.start.diff(b.start); if (startDiff !== 0) { return startDiff; } return b.end.diff(b.start) - a.end.diff(a.start); }); const sortedSingleDay = [...singleDayEvents].sort((a, b) => a.start.diff(b.start)); const grid = []; for (let row = 0;row < dayMaxEvents; row++) { grid[row] = []; for (let col = 0;col < 7; col++) { grid[row][col] = { taken: false, event: null }; } } const processedEvents = []; for (const event of sortedMultiDay) { const eventStart = dayjs_config_default.max(event.start.startOf("day"), weekStart); const eventEnd = dayjs_config_default.min(event.end.startOf("day"), weekEnd); const startCol = Math.max(0, eventStart.diff(weekStart, "day")); const endCol = Math.min(6, eventEnd.diff(weekStart, "day")); let placedSuccessfully = false; let assignedRow = -1; for (let row = 0;row < dayMaxEvents; row++) { let canPlace = true; for (let col = startCol;col <= endCol; col++) { if (grid[row][col].taken) { canPlace = false; break; } } if (canPlace) { assignedRow = row; break; } } if (assignedRow !== -1) { for (let col = startCol;col <= endCol; col++) { grid[assignedRow][col] = { taken: true, event }; } const spanDays = endCol - startCol + 1; processedEvents.push({ left: startCol / 7 * 100, width: spanDays / 7 * 100, top: DAY_NUMBER_HEIGHT + GAP_BETWEEN_ELEMENTS + assignedRow * (EVENT_BAR_HEIGHT + GAP_BETWEEN_ELEMENTS), height: EVENT_BAR_HEIGHT, position: assignedRow, ...event }); placedSuccessfully = true; } if (!placedSuccessfully) { for (let tryStartCol = startCol + 1;tryStartCol <= endCol; tryStartCol++) { let truncatedAssignedRow = -1; for (let row = 0;row < dayMaxEvents; row++) { let canPlace = true; for (let col = tryStartCol;col <= endCol; col++) { if (grid[row][col].taken) { canPlace = false; break; } } if (canPlace) { truncatedAssignedRow = row; break; } } if (truncatedAssignedRow !== -1) { for (let col = tryStartCol;col <= endCol; col++) { grid[truncatedAssignedRow][col] = { taken: true, event }; } const truncatedSpanDays = endCol - tryStartCol + 1; processedEvents.push({ left: tryStartCol / 7 * 100, width: truncatedSpanDays / 7 * 100, top: DAY_NUMBER_HEIGHT + GAP_BETWEEN_ELEMENTS + truncatedAssignedRow * (EVENT_BAR_HEIGHT + GAP_BETWEEN_ELEMENTS), height: EVENT_BAR_HEIGHT, position: truncatedAssignedRow, ...event }); placedSuccessfully = true; break; } } } } for (const event of sortedSingleDay) { const eventStart = dayjs_config_default.max(event.start.startOf("day"), weekStart); const col = Math.max(0, eventStart.diff(weekStart, "day")); let assignedRow = -1; for (let row = 0;row < dayMaxEvents; row++) { if (!grid[row][col].taken) { assignedRow = row; break; } } if (assignedRow !== -1) { grid[assignedRow][col] = { taken: true, event }; processedEvents.push({ left: col / 7 * 100, width: 1 / 7 * 100, top: DAY_NUMBER_HEIGHT + GAP_BETWEEN_ELEMENTS + assignedRow * (EVENT_BAR_HEIGHT + GAP_BETWEEN_ELEMENTS), height: EVENT_BAR_HEIGHT, position: assignedRow, ...event }); } } return processedEvents; }; // src/features/month-view/components/week-events-layer/week-events-layer.tsx import { jsx as jsx19 } from "react/jsx-runtime"; var WeekEventsLayer = ({ days }) => { const weekStart = days[0]; const processedWeekEvents = useProcessedWeekEvents({ days }); return /* @__PURE__ */ jsx19("div", { className: "relative w-full h-full pointer-events-none z-20 overflow-clip", children: processedWeekEvents.map((event) => { return /* @__PURE__ */ jsx19("div", { className: "absolute z-10 pointer-events-auto overflow-clip", style: { left: `calc(${event.left}% + var(--spacing) * 0.25)`, w