SCALE — Build Lab
UI部品 · REACT COMPONENT

月間カレンダービュー

CATEGORYUI部品 TYPEReact Component EFFORT90〜180分 DIFFICULTY
PRIMARY CODE
tsx
'use client';
import { useState } from 'react';

export function MonthView({ events }: { events: { date: string; title: string }[] }) {
  const [cur, setCur] = useState(new Date());
  const year = cur.getFullYear();
  const month = cur.getMonth();
  const firstDay = new Date(year, month, 1).getDay();
  const lastDate = new Date(year, month + 1, 0).getDate();
  const cells = [...Array(firstDay).fill(null), ...Array.from({ length: lastDate }, (_, i) => i + 1)];

  return (
    <div>
      <button onClick={() => setCur(new Date(year, month - 1))}>‹</button>
      <span>{year}年{month + 1}月</span>
      <button onClick={() => setCur(new Date(year, month + 1))}>›</button>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)' }}>
        {['日','月','火','水','木','金','土'].map(d => <div key={d}>{d}</div>)}
        {cells.map((d, i) => {
          if (!d) return <div key={i} />;
          const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
          const dayEvents = events.filter(e => e.date === dateStr);
          return (
            <div key={i} style={{ minHeight: 80, border: '1px solid #333', padding: 4 }}>
              <div>{d}</div>
              {dayEvents.slice(0, 3).map((e, j) => <div key={j} style={{ fontSize: 11, opacity: .7 }}>{e.title}</div>)}
            </div>
          );
        })}
      </div>
    </div>
  );
}
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • 任意のダッシュボードに組み込み

月間カレンダービュー

:LiTarget: 用途

月間カレンダーUI。各日にイベント数・タイトル表示。クリックで詳細。

:LiSparkle: 特徴

  • 月切替
  • イベントドット/タイトル
  • 今日強調
  • ホバー詳細

:LiCode: コード(コピペ用)

'use client';
import { useState } from 'react';

export function MonthView({ events }: { events: { date: string; title: string }[] }) {
  const [cur, setCur] = useState(new Date());
  const year = cur.getFullYear();
  const month = cur.getMonth();
  const firstDay = new Date(year, month, 1).getDay();
  const lastDate = new Date(year, month + 1, 0).getDate();
  const cells = [...Array(firstDay).fill(null), ...Array.from({ length: lastDate }, (_, i) => i + 1)];

  return (
    <div>
      <button onClick={() => setCur(new Date(year, month - 1))}>‹</button>
      <span>{year}年{month + 1}月</span>
      <button onClick={() => setCur(new Date(year, month + 1))}>›</button>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)' }}>
        {['日','月','火','水','木','金','土'].map(d => <div key={d}>{d}</div>)}
        {cells.map((d, i) => {
          if (!d) return <div key={i} />;
          const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
          const dayEvents = events.filter(e => e.date === dateStr);
          return (
            <div key={i} style={{ minHeight: 80, border: '1px solid #333', padding: 4 }}>
              <div>{d}</div>
              {dayEvents.slice(0, 3).map((e, j) => <div key={j} style={{ fontSize: 11, opacity: .7 }}>{e.title}</div>)}
            </div>
          );
        })}
      </div>
    </div>
  );
}

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加