shadimage
wigggle-ui
componentcalendar-lg-03source: ok · hit

Calendar Lg 03

npx shadcn add "https://wigggle-ui.vercel.app/r/calendar-lg-03.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 · light
iconslucide-react

Registry dependencies

https://wigggle-ui.vercel.app/r/widget.jsonhttps://wigggle-ui.vercel.app/r/label.jsonhttps://wigggle-ui.vercel.app/r/button.jsonhttps://wigggle-ui.vercel.app/r/avatar.json

Files1 · source cached 10/8/2026, 12:19:02 PM

registry/default/widgets/calendar/lg/calendar-03.tsx
import React from "react";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";

import {
  Widget,
  WidgetContent,
  WidgetHeader,
  WidgetTitle,
} from "@/registry/default/ui/widget";
import { Label } from "@/registry/default/ui/label";
import { Button } from "@/registry/default/ui/button";
import {
  Avatar,
  AvatarFallback,
  AvatarImage,
} from "@/registry/default/ui/avatar";

export default function WidgetDemo() {
  const now = new Date();

  const monthName = now.toLocaleDateString("en-US", { month: "long" });
  const date = now.getDate();

  const [selectedDate, setSelectedDate] = React.useState<number>(now.getDate());

  const weekDates = Array.from(
    { length: 7 },
    (_, i) =>
      new Date(
        now.getFullYear(),
        now.getMonth(),
        now.getDate() - now.getDay() + i,
      ),
  );

  const lowerLimit = now.getDate() - 3;
  const upperLimit = now.getDate() + 3;

  const allTasks = [
    {
      imageURL: "https://avatars.githubusercontent.com/u/6751787?v=4",
      title: "Teach Theo Elixir",
      time: "8:30 AM",
    },
    {
      imageURL: "https://avatars.githubusercontent.com/u/124599?v=4",
      title: "Meeting with ShadCN",
      time: "12:30 PM",
    },
    {
      imageURL: "https://owcdn.net/img/62bbebb185a7e.png",
      title: "PRX vs G2",
      time: "3:30 PM",
    },
    {
      imageURL: "https://avatars.githubusercontent.com/u/7549148?v=4",
      title: "Warcraft with Orc! ⚔️",
      time: "7:30 PM",
    },
    {
      imageURL: "https://avatars.githubusercontent.com/u/23276437?v=4",
      title: "Teaching Tailwind",
      time: "10:00 AM",
    },
    {
      imageURL: "https://avatars.githubusercontent.com/u/14985020?s=200&v=4",
      title: "Wigggle UI Vercel OSS",
      time: "4:30 PM",
    },
  ];

  const getRandomTasks = () => {
    const count = Math.random() > 0.5 ? 2 : 3;
    const indices = new Set<number>();
    while (indices.size < count) {
      indices.add(Math.floor(Math.random() * allTasks.length));
    }
    return Array.from(indices).map((i) => allTasks[i]);
  };

  const tasks = getRandomTasks();

  return (
    <Widget size="lg" className="p-0">
      <WidgetHeader className="bg-destructive rounded-t-3xl p-4">
        <WidgetTitle className="w-max text-center text-2xl">
          {date} {monthName}
        </WidgetTitle>
        <Button
          onClick={() => {
            setSelectedDate(now.getDate());
          }}
          variant="secondary"
          size="sm"
        >
          Today
        </Button>
      </WidgetHeader>
      <WidgetContent className="flex-col items-center justify-start gap-4 p-4">
        <div className="flex w-full items-center justify-between gap-2">
          <Button
            onClick={() => {
              selectedDate > lowerLimit
                ? setSelectedDate(selectedDate - 1)
                : setSelectedDate(lowerLimit);
            }}
            variant="outline"
            size="icon-sm"
          >
            <ChevronLeftIcon />
          </Button>
          <div className="grid w-full grid-cols-7 items-center gap-x-4 px-3">
            {["S", "M", "T", "W", "T", "F", "S"].map((day, i) => (
              <Label
                key={`weekday-${i}-${day}`}
                className="text-muted-foreground mx-auto text-sm"
              >
                {day}
              </Label>
            ))}
            {weekDates.map((d, i) => {
              const isToday = d.getDate() === selectedDate;
              return (
                <Label
                  key={i}
                  className={`mx-auto text-xl ${isToday ? "text-destructive" : ""}`}
                >
                  {d.getDate()}
                </Label>
              );
            })}
          </div>
          <Button
            onClick={() => {
              selectedDate < upperLimit
                ? setSelectedDate(selectedDate + 1)
                : setSelectedDate(upperLimit);
            }}
            variant="outline"
            size="icon-sm"
          >
            <ChevronRightIcon />
          </Button>
        </div>
        {tasks.map((el) => (
          <div
            key={el.title}
            className="flex w-full items-center justify-start gap-x-4 pb-3 not-last:border-b-2"
          >
            <Avatar size="lg">
              <AvatarImage src={el.imageURL} alt={el.title} />
              <AvatarFallback>{el.title.slice(0, 1)}</AvatarFallback>
            </Avatar>
            <div className="space-y-1">
              <Label className="text-base">{el.title}</Label>
              <Label className="text-muted-foreground font-normal">
                {el.time}
              </Label>
            </div>
          </div>
        ))}
      </WidgetContent>
    </Widget>
  );
}

More from wigggle-ui

Browse all components in this library →