4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ReactAdvent Calendar 2024

Day 23

uiw/react-heat-map + GitHub APIを用いてReactでGitHubの草 (Contribution) を作ってみよう

Posted at

概要

ReactでGitHubの草(Contribution)を再現してみる記事になります。

スクリーンショット 2024-12-21 13.34.24.png

使用するライブラリ

@uiw/react-heat-map を使います。

他にもReactでヒートマップを作成できるライブラリはあるのですが、2024年12月現在、定期的に更新されているライブラリとしてこちらを選定しました。

実装

Heatmap.tsx
import { ContributionValue, GitHubContributionWeeks } from "@/types/contribution";
import HeatMap from '@uiw/react-heat-map';
import { useEffect, useState } from "react";

export const Heatmap = () => {

  const nowDate = new Date();
  const heatmapStartDate = nowDate.setMonth(nowDate.getMonth() - 4);
  const [heatmapValue, setHeatmapValue] = useState<ContributionValue[]>([{date: heatmapStartDate.toString(), count: 0}]);


  // GitHub APIに送信する情報
  const USERNAME = process.env.GITHUB_USERNAME;
  const headers = {
    'Authorization': `bearer ${process.env.GITHUB_TOKEN}`,
  }
  const body = {
    "query": `query {
        user(login: "${USERNAME}"){
          contributionsCollection {
            contributionCalendar {
              totalContributions
              weeks {
                contributionDays {
                  contributionCount
                  date
                }
              }
            }
          }
        }
      }`
  }
  
  const generateContributionValues = (weeks: GitHubContributionWeeks) => {
    const contributionValues: ContributionValue[] = [];

    for (const week of weeks) {
      week.contributionDays.map((obj) => {
        contributionValues.push({count: obj.contributionCount, date: obj.date})
      })
    }

    return contributionValues;
  }

  useEffect(() => {
    // コンポーネント読み込み時にGitHubからContributionの情報を取得する
    const fetchData = async () => {
      const response = await fetch('https://api.github.com/graphql', 
        { 
          method: 'POST', 
          body: JSON.stringify(body), 
          headers: headers 
        });
      return response;
    };

    (async() => {
      const response = await fetchData();
      const data = await response.json();
      const weeks = data.data.user.contributionsCollection.contributionCalendar.weeks;
      const contributionValues = generateContributionValues(weeks);
      setHeatmapValue(contributionValues);
    })();
  }, []);


  return (
    <div className="heatmap">
      <HeatMap
        value={heatmapValue} 
        startDate={new Date(heatmapStartDate)}
        endDate={new Date()}
        panelColors={{
          1: '#161b22',
          2: '#0e4429',
          3: '#006d32',
          5: '#26a641',
          8: '#39d353'
        }}
      />
    </div>
  );
};


自身のGitHubからContribution情報をとってくる

.envファイルに自身のGitHubの認証情報を記載し、GitHub GraphQL APIでContribution情報を取得します。

const USERNAME = process.env.GITHUB_USERNAME;
const headers = {
  'Authorization': `bearer ${process.env.GITHUB_TOKEN}`,
}
const body = {
  "query": `query {
      user(login: "${USERNAME}"){
        contributionsCollection {
          contributionCalendar {
            totalContributions
            weeks {
              contributionDays {
                contributionCount
                date
              }
            }
          }
        }
      }
    }`
}

コンポーネント読み込み時にGitHub GraphQL APIを呼び出します。

  useEffect(() => {
    // コンポーネント読み込み時にGitHubからContributionの情報を取得する
    const fetchData = async () => {
      const response = await fetch('https://api.github.com/graphql', 
        { 
          method: 'POST', 
          body: JSON.stringify(body), 
          headers: headers 
        });
      return response;
    };

    (async() => {
      const response = await fetchData();
      const data = await response.json();
      const weeks = data.data.user.contributionsCollection.contributionCalendar.weeks;
      const contributionValues = generateContributionValues(weeks);
      setHeatmapValue(contributionValues);
    })();
  }, []);

APIのレスポンスからContributionの情報をヒートマップで利用できるように成型してあげます。


  const generateContributionValues = (weeks: GitHubContributionWeeks) => {
    const contributionValues: ContributionValue[] = [];

    for (const week of weeks) {
      week.contributionDays.map((obj) => {
        contributionValues.push({count: obj.contributionCount, date: obj.date})
      })
    }

    return contributionValues;
  }

  /* 以下のようなオブジェクトの配列に成型する
    [
        {
            count: 0
            date: "2024-12-01"
        },
        {
            count: 1
            date: "2024-12-02"
        },
        ...
    ]
  */

heatmapValueに成型したデータをsetします。

setHeatmapValue(contributionValues);

ヒートマップ

panelColorsプロパティで、Contributionの数でパネルの色を変化させることができます。

  return (
    <div className="heatmap">
      <HeatMap
        value={heatmapValue} // GitHubから取得したContributionデータ(成型済)をvalueとして持たせる
        startDate={new Date(heatmapStartDate)}
        endDate={new Date()}
        panelColors={{
          1: '#161b22',
          2: '#0e4429',
          3: '#006d32',
          5: '#26a641',
          8: '#39d353'
        }}
      />
    </div>
  );

GitHubのContributionを再現することができました!

スクリーンショット 2024-12-18 7.05.36.png

おまけ: Tooltipの実装

react-tooltip を活用すれば、ホバー時にContribution数を表示させることができます。

スクリーンショット 2024-12-21 13.44.26.png

  import { Tooltip } from 'react-tooltip';
  
  return (
    <div className={styles["activity"]}>
      <HeatMap
        value={heatmapValue} 
        startDate={new Date(heatmapStartDate)}
        endDate={new Date()}
        panelColors={{
          1: '#161b22',
          2: '#0e4429',
          3: '#006d32',
          5: '#26a641',
          8: '#39d353'
        }}
        rectRender={(props, data) => {
          if (!data.count) return <rect {...props} />;
          return (
            <>
              <rect data-tooltip-id={`heatmap`} onMouseEnter={() => setTooltipData({...data})} {...props} />
            </>
          );
        }}
      />
      <Tooltip id={`heatmap`} content={`count: ${tooltipData.count} date: ${tooltipData.date}`}/>
    </div>
  );

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?