Tips & Tricks

如何Build a Calendar Component:Claude Code 实战指南

学习如何build a calendar component:Claude Code 实战. 包含实用代码示例和分步指导。

カレンダー组件の需要

予約システム、スケジュール管理、日期选择など、カレンダーUIは多くの应用で是必要的。外部库に頼ることも可以が、デザインや機能の自由度を求めるなら自作が最適です。借助 Claude Code,柔軟なカレンダー组件を快速构建可以。

月显示カレンダーの基本实现

> date-fns使用...的月显示カレンダー组件を作って。
> 月の切换と日期选择に支持して。
import { useState } from 'react';
import {
  startOfMonth, endOfMonth, startOfWeek, endOfWeek,
  eachDayOfInterval, format, addMonths, subMonths, isSameMonth, isSameDay, isToday,
} from 'date-fns';
import { ja } from 'date-fns/locale';

interface CalendarProps {
  selected?: Date;
  onSelect: (date: Date) => void;
  events?: { date: Date; title: string }[];
}

function Calendar({ selected, onSelect, events = [] }: CalendarProps) {
  const [currentMonth, setCurrentMonth] = useState(new Date());

  const monthStart = startOfMonth(currentMonth);
  const monthEnd = endOfMonth(currentMonth);
  const calendarStart = startOfWeek(monthStart, { locale: ja });
  const calendarEnd = endOfWeek(monthEnd, { locale: ja });
  const days = eachDayOfInterval({ start: calendarStart, end: calendarEnd });

  const weekDays = ['日', '月', '火', '水', '木', '金', '土'];

  const getEventsForDay = (day: Date) =>
    events.filter((e) => isSameDay(e.date, day));

  return (
    <div className="w-full max-w-md mx-auto" role="application" aria-label="calendar">
      <div className="flex items-center justify-between mb-4">
        <button onClick={() => setCurrentMonth(subMonths(currentMonth, 1))} aria-label="前月">

        </button>
        <h2 className="text-lg font-bold" aria-live="polite">
          {format(currentMonth, 'yyyy年M月', { locale: ja })}
        </h2>
        <button onClick={() => setCurrentMonth(addMonths(currentMonth, 1))} aria-label="翌月">

        </button>
      </div>

      <div className="grid grid-cols-7 gap-px" role="grid">
        {weekDays.map((day) => (
          <div key={day} className="text-center text-sm font-medium text-gray-500 py-2" role="columnheader">
            {day}
          </div>
        ))}

        {days.map((day) => {
          const dayEvents = getEventsForDay(day);
          const isSelected = selected && isSameDay(day, selected);
          const inMonth = isSameMonth(day, currentMonth);

          return (
            <button
              key={day.toISOString()}
              onClick={() => onSelect(day)}
              role="gridcell"
              aria-selected={isSelected}
              aria-label={`${format(day, 'M月d日', { locale: ja })}${dayEvents.length > 0 ? ` ${dayEvents.length}件の予定` : ''}`}
              className={`p-2 text-center rounded-lg relative ${
                !inMonth ? 'text-gray-300' :
                isSelected ? 'bg-blue-600 text-white' :
                isToday(day) ? 'bg-blue-100 font-bold' :
                'hover:bg-gray-100'
              }`}
            >
              {format(day, 'd')}
              {dayEvents.length > 0 && (
                <span className="absolute bottom-1 left-1/2 -translate-x-1/2 w-1 h-1 rounded-full bg-blue-500" />
              )}
            </button>
          );
        })}
      </div>
    </div>
  );
}

日期範囲选择

> チェックイン・チェックアウト这样的日期範囲选择に支持して。
function useDateRange() {
  const [range, setRange] = useState<{ start: Date | null; end: Date | null }>({
    start: null,
    end: null,
  });

  const handleSelect = (date: Date) => {
    if (!range.start || (range.start && range.end)) {
      setRange({ start: date, end: null });
    } else if (date < range.start) {
      setRange({ start: date, end: range.start });
    } else {
      setRange({ start: range.start, end: date });
    }
  };

  const isInRange = (date: Date) => {
    if (!range.start || !range.end) return false;
    return date >= range.start && date <= range.end;
  };

  return { range, handleSelect, isInRange };
}

事件显示付きカレンダー

function EventCalendar({ events }: { events: CalendarEvent[] }) {
  const [selectedDate, setSelectedDate] = useState<Date>(new Date());
  const dayEvents = events.filter((e) => isSameDay(e.date, selectedDate));

  return (
    <div className="flex gap-4">
      <Calendar selected={selectedDate} onSelect={setSelectedDate} events={events} />
      <div className="flex-1">
        <h3 className="font-bold mb-2">
          {format(selectedDate, 'M月d日(E)', { locale: ja })}の予定
        </h3>
        {dayEvents.length === 0 ? (
          <p className="text-gray-500">予定はありません</p>
        ) : (
          <ul className="space-y-2">
            {dayEvents.map((event) => (
              <li key={event.id} className="p-3 rounded-lg border">
                <span className="font-medium">{event.title}</span>
                <span className="text-sm text-gray-500 ml-2">
                  {format(event.date, 'HH:mm')}
                </span>
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>
  );
}

总结

借助 Claude Code,月显示・日期範囲选择・事件显示など、用途に合ったカレンダー组件を柔軟に构建可以。表格显示との組み合わせは表格组件を、响应式支持は响应式デザイン

date-fns的详细信息请参阅date-fns官方网站

#Claude Code #calendar #React #UI #date-fns