wigggle-ui
componentcalendar-lg-01source: ok · hit
Calendar Lg 01
npx shadcn add "https://wigggle-ui.vercel.app/r/calendar-lg-01.json"free
Where it goes
calendar
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lighticonslucide-react
Registry dependencies
https://wigggle-ui.vercel.app/r/widget.jsonhttps://wigggle-ui.vercel.app/r/button.jsonhttps://wigggle-ui.vercel.app/r/separator.jsonhttps://wigggle-ui.vercel.app/r/label.jsonhttps://wigggle-ui.vercel.app/r/utils.json
Files1 · source cached 10/8/2026, 12:51:26 PM
registry/default/widgets/calendar/lg/calendar-01.tsx
"use client";
import React from "react";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
import {
Widget,
WidgetContent,
WidgetHeader,
WidgetTitle,
} from "@/registry/default/ui/widget";
import { Button } from "@/registry/default/ui/button";
import { Separator } from "@/registry/default/ui/separator";
import { Label } from "@/registry/default/ui/label";
import { cn } from "@/registry/default/lib/utils";
const WEEKDAY_LABELS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const MIN_PRICE = 80;
const MAX_PRICE = 250;
export default function WidgetDemo() {
const [date, setDate] = React.useState(new Date());
const year = date.getFullYear();
const month = date.getMonth();
const calendarDays = React.useMemo(() => {
const firstDay = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
return [
...Array(firstDay).fill(null),
...Array.from({ length: daysInMonth }, (_, i) => i + 1),
];
}, [year, month]);
const prices = React.useMemo(() => {
const priceMap: Record<number, number> = {};
calendarDays.forEach((day) => {
if (day !== null) {
priceMap[day] =
Math.floor(Math.random() * (MAX_PRICE - MIN_PRICE + 1)) + MIN_PRICE;
}
});
return priceMap;
}, [calendarDays]);
const handlePreviousMonth = () =>
setDate((prev) => new Date(prev.getFullYear(), prev.getMonth() - 1, 1));
const handleNextMonth = () =>
setDate((prev) => new Date(prev.getFullYear(), prev.getMonth() + 1, 1));
const monthName = new Intl.DateTimeFormat("en-US", { month: "short" }).format(
new Date(year, month),
);
return (
<Widget className="gap-3" size="lg">
<WidgetHeader className="items-center justify-between">
<Button variant="outline" size="icon-sm" onClick={handlePreviousMonth}>
<ChevronLeftIcon className="size-4" />
</Button>
<WidgetTitle className="text-2xl">
{monthName} {year}
</WidgetTitle>
<Button variant="outline" size="icon-sm" onClick={handleNextMonth}>
<ChevronRightIcon className="size-4" />
</Button>
</WidgetHeader>
<Separator />
<WidgetContent className="flex-col! items-stretch! justify-start!">
<div className="grid h-full w-full auto-rows-fr grid-cols-7 gap-2">
{WEEKDAY_LABELS.map((day, i) => (
<div
key={`weekday-${i}`}
className="text-muted-foreground m-auto text-center text-sm font-semibold"
>
{day}
</div>
))}
{calendarDays.map((day, i) => (
<div
key={i}
className="flex flex-col items-center justify-center gap-0.5 rounded-lg text-center"
>
{day ? (
<>
<Label className="text-sm leading-none font-semibold">
{day}
</Label>
<Label
className={cn(
prices[day] < 120
? "text-productive"
: "text-muted-foreground",
"text-xs leading-tight",
)}
>
${prices[day]}
</Label>
</>
) : (
<></>
)}
</div>
))}
</div>
</WidgetContent>
</Widget>
);
}