アプリのカレンダーに日付ハイライトをつける


# はじめに
現在、ジャーナルアプリを個人開発をしている最中です。
その中で、カレンダーにジャーナルを書いた日をハイライトさせる実装をつけることになりました。

この部分について他の人が共有していないか探しましたが見つけることができなかったので、実装内容を簡単にご紹介しながら共有をしたいと思います！

# 使用する技術
```
・環境
-Vite

・言語
- TypeScript
- React

・CSS
-Tailwind
-Shadcn
```

### Shadcnとは
知っている方も多いと思いますが、ShadcnはTailwindを使用したCSSコンポーネントが集約されているサイトになります。
気に入ったコンポーネントのコードをコピーして自分の環境ひペースとすれば簡単に綺麗なUIを取り入れることができます。

https://ui.shadcn.com/docs

今回はこのShadcnのCalendarを使用します。


# 前準備
ご紹介した上記のShadcnから「Calendar」を検索して紹介されているコードをカレンダーを表示させたい部分に貼り付けします。
以下がそのコードです。

```Typescript
import { useState } from "react";

import { Calendar } from "../@/components/ui/calendar";

function CalendarPage() {
  const [date, setDate] = useState<Date | undefined>(new Date());
  return (
    <div>
      <Calendar
        mode="single"
        selected={date}
        onSelect={setDate}
        className="rounded-md border"
      />
    </div>
  );
}

export default CalendarPage;
```

# ハイライトの実装
ここからハイライトの実装に入ります。
今回はsupabaseをDBとして利用していて、そこにあるデータでハイライトする日を決めます。

```Typescript
import React, { useState, useEffect } from "react";
import { Calendar } from "@/components/ui/calendar";
import {
  fetchHighlightedDates,
  handleDateSelect,
} from "../utils/supabaseFunction";

interface DateItem {
  created_at: string;
}

interface ContentItem {
  content: string;
}

function CalendarPage() {
  const [date, setDate] = useState<Date | undefined>(new Date());
  const [highlightedDates, setHighlightedDates] = useState<Date[]>([]);

  useEffect(() => {
    const fetchDates = async () => {
      try {
        const dates = await fetchHighlightedDates();
        if (Array.isArray(dates)) {
          setHighlightedDates(dates.map((item: DateItem) => new Date(item.created_at)));
      } catch (error) {
        console.error("コンテンツの取得中にエラーが発生しました", error);
      }
    };
    fetchDates();
  }, []);


  return (
    <div className="p-4">
      <Calendar
        mode="single"
        selected={date}
        className="rounded-md border"
        modifiers={{
          highlighted: highlightedDates,
        }}
        modifiersStyles={{
          highlighted: { backgroundColor: '#90cdf4' }
        }}
      />
    </div>
  );
}

export default CalendarPage;

```

fetchHighlightedDates関数ではsupabaseに登録されたジャーナルが書かれた日付のデータを取得してきます。今回の実装では日付だけではなくジャーナルのデータなども取得してきているので、データが配列になっています。
配列のデータをuseStateのセッターであるsetHighlightedDatesに渡すとき、日付のデータのみを取り出して渡します。

```
 setHighlightedDates(dates.map((item: DateItem) => new Date(item.created_at)));

 ```

 日付情報が入ったhighlightedDatesをreturnの中でmodifiersプロパティを使用して渡します。modifiersStylesはmodifiersプロパティで指定した日付にスタイルを当てることができます。ここでハイライトのカラーを決めます。

 ```Typescript
     modifiers={{
          highlighted: highlightedDates,
        }}
     modifiersStyles={{
          highlighted: { backgroundColor: '#90cdf4' }
        }}
```

# おまけ
今回のアプリではハイライトした日付をクリックしたら、カレンダー下にジャーナル内容を表示させるように設定しています。
以下が更新済みのreturn文です。
 ```Typescript

return (
    <div>
      <Calendar
        mode="single"
        selected={date}
        onSelect={(newDate: Date | undefined) => onDateSelect(newDate)}
        className="rounded-md border"
        modifiers={{
          highlighted: highlightedDates,
        }}
        modifiersStyles={{
          highlighted: { backgroundColor: "#90cdf4" },
        }}
      />
      {selectedContent && (
        <div className="mt-4 p-4 bg-gray-100 rounded-md">
          <h3 className="font-bold">選択された日付のコンテンツ:</h3>
          <p>{selectedContent}</p>
        </div>
      )}
    </div>
  );
 ```
 CalendarコンポーネントにonSelectプロパティを追加してonDateSelect関数を渡しています。onSelectプロパティは日付が選択された時に呼び出される関数を指定することができます。
呼び出している関数はこちらです。

```Typescript
const [selectedContent, setSelectedContent] = useState<string[] | null>(null);

const onDateSelect = async (selectedDate: Date | undefined) => {
    setDate(selectedDate);
    if (selectedDate) {
      try {
        const content = await handleDateSelect(selectedDate);
        setSelectedContent(content || null);
      } catch (error) {
        console.error(
          "選択された日付のコンテンツ取得中にエラーが発生しました:",
          error
        );
      }
    }
  };
```
 handleDateSelect関数では指定した日付のジャーナルデータを返すようにしています。
 返されたデータのジャーナル部分を取り出してSelectedContentのセッターに渡して値をセットしています。

 最後に値が更新されたselectedContentをreturn文の中で表示させると、ハイライトされた日付を押下した時にカレンダー下にジャーナルデータを表示させることができます。
 
 ```Typescript
       {selectedContent && (
        <div className="mt-4 p-4 bg-gray-100 rounded-md">
          <h3 className="font-bold">選択された日付のコンテンツ:</h3>
          <p>{selectedContent}</p>
        </div>
      )}
```

# おわりに
実装前は難しいイメージを持っていましたが、実際にやってみると案外シンプルにやりたいことを実現できました。アプリ内でカレンダーを取り入れたい方の参考になれば幸いです！

# JISOUのメンバー募集中！

プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
実践的なカリキュラムで、あなたのエンジニアとしてのキャリアを最短で飛躍させましょう！
興味のある方は、ぜひホームページからお気軽にカウンセリングをお申し込みください！
▼▼▼

https://projisou.jp/