-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #230 from mobeigi/add-development-activity-section…
…-homepage Add development activity section to homepage
- Loading branch information
Showing
29 changed files
with
450 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
'use client'; | ||
|
||
import { useTheme } from 'next-themes'; | ||
import { BaseTooltipWrapper, StyledTooltip } from './styled'; | ||
import { resolvedThemeToThemeMode } from '@/utils/theme'; | ||
import { BaseTooltipProps } from './types'; | ||
|
||
// Provides a base tooltip that has been styled. | ||
// It is best practice to store one copy of this tooltip and reuse it for performance and to avoid various DOM render bugs | ||
export const BaseTooltip = ({ id, isOpen }: BaseTooltipProps) => { | ||
const { resolvedTheme } = useTheme(); | ||
const resolvedThemeMode = resolvedThemeToThemeMode(resolvedTheme); | ||
|
||
return ( | ||
<BaseTooltipWrapper> | ||
<StyledTooltip id={id} place="bottom" opacity={1.0} className={`rt-theme-${resolvedThemeMode}`} isOpen={isOpen} /> | ||
</BaseTooltipWrapper> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { BaseTooltip as default } from './BaseTooltip'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface BaseTooltipProps { | ||
id: string; | ||
isOpen?: boolean; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
170 changes: 170 additions & 0 deletions
170
app/src/components/GitContributionGraph/GitContributionGraph.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
'use client'; | ||
|
||
import React, { useState, useRef, useCallback, useEffect } from 'react'; | ||
import { useTheme } from 'next-themes'; | ||
import { ThemeMode } from '@/types/theme'; | ||
import { resolvedThemeToThemeMode } from '@/utils/theme'; | ||
import { format as formatDate } from 'date-fns'; | ||
import BaseTooltip from '../BaseTooltip'; | ||
import { GitContributionGraphProps } from './types'; | ||
import ClipLoader from 'react-spinners/ClipLoader'; | ||
import { GitContributionGraphContainer, SpinnerWrapper } from './styled'; | ||
|
||
import * as echarts from 'echarts/core'; | ||
import type { CallbackDataParams } from 'echarts/types/src/util/types.js'; | ||
import ReactEChartsCore from 'echarts-for-react/lib/core'; | ||
import { HeatmapChart } from 'echarts/charts'; | ||
import { TooltipComponent, VisualMapComponent, CalendarComponent } from 'echarts/components'; | ||
import { SVGRenderer } from 'echarts/renderers'; | ||
|
||
echarts.use([HeatmapChart, TooltipComponent, VisualMapComponent, CalendarComponent, SVGRenderer]); | ||
|
||
const tooltipId = 'git-contribution-graph-tooltip'; | ||
|
||
export const GitContributionGraph = ({ data }: GitContributionGraphProps) => { | ||
const [isLoaded, setIsLoaded] = useState(false); | ||
const chartRef = useRef<ReactEChartsCore>(null); | ||
const tooltipRef = useRef<HTMLDivElement | null>(null); | ||
const [tooltipContent, setTooltipContent] = useState<string | null>(null); | ||
const [isTooltipOpen, setIsTooltipOpen] = useState(false); | ||
|
||
const { resolvedTheme } = useTheme(); | ||
const resolvedThemeMode = resolvedThemeToThemeMode(resolvedTheme); | ||
|
||
useEffect(() => { | ||
setIsLoaded(true); | ||
}, []); | ||
|
||
// Set start/end dates based on incoming data | ||
const startDate = data[0][0]; | ||
const endDate = data[data.length - 1][0]; | ||
|
||
const option: echarts.EChartsCoreOption = { | ||
backgroundColor: 'transparent', // avoid darkMode theme background colour | ||
tooltip: { show: false }, | ||
visualMap: { | ||
show: false, | ||
min: 0, | ||
max: 20, | ||
inRange: { | ||
color: | ||
resolvedThemeMode === ThemeMode.Dark | ||
? ['#3a2e2f', '#5e2f33', '#84373a', '#af3a3d', '#fb4d56'] | ||
: ['#ffe5e7', '#ffb7bb', '#ff8a91', '#ff5c66', '#fb4d56'], | ||
}, | ||
type: 'piecewise', | ||
splitNumber: 5, | ||
pieces: [{ min: 20 }, { min: 10, max: 20 }, { min: 3, max: 10 }, { min: 1, max: 3 }, { value: 0 }], | ||
}, | ||
calendar: { | ||
top: 20, | ||
left: 50, | ||
right: 5, | ||
cellSize: [20], | ||
range: [startDate, endDate], | ||
itemStyle: { | ||
borderWidth: 0, | ||
color: 'transparent', | ||
}, | ||
dayLabel: { | ||
nameMap: ['', 'Mon', '', 'Wed', '', 'Fri', ''], | ||
}, | ||
yearLabel: { show: false }, | ||
splitLine: { show: false }, | ||
}, | ||
series: { | ||
type: 'heatmap', | ||
coordinateSystem: 'calendar', | ||
itemStyle: { | ||
borderRadius: 6, | ||
borderWidth: 2, | ||
borderColor: 'var(--theme-background)', // should match background colour of the chart container | ||
}, | ||
data: data, | ||
}, | ||
}; | ||
|
||
/** | ||
* We rely on this function to update the tooltip position and content, triggered by the mouseover event. | ||
*/ | ||
const updateTooltip = useCallback((params: CallbackDataParams) => { | ||
if (!params.value || !chartRef.current) { | ||
return; | ||
} | ||
|
||
const chart = chartRef.current.getEchartsInstance(); | ||
if (!chart) { | ||
return; | ||
} | ||
|
||
const [x, y] = chart.convertToPixel({ seriesIndex: 0 }, params.value as number[]); | ||
|
||
// Get the bounding box of the ECharts container | ||
const echartsContainerRect = chart.getDom().getBoundingClientRect(); | ||
|
||
// Adjust to obtain absolute page coordinates | ||
const absoluteX = window.scrollX + echartsContainerRect.left + x; | ||
const absoluteY = window.scrollY + echartsContainerRect.top + y; | ||
|
||
if (tooltipRef.current) { | ||
tooltipRef.current.style.left = `${absoluteX}px`; | ||
tooltipRef.current.style.top = `${absoluteY}px`; | ||
} | ||
|
||
const [date, contributionCount] = params.value as [string, number]; | ||
const dateString = formatDate(date, 'd MMMM yyyy'); | ||
|
||
setTooltipContent( | ||
contributionCount > 0 | ||
? `${contributionCount} contributions on ${dateString}` | ||
: `No contributions on ${dateString}`, | ||
); | ||
setIsTooltipOpen(true); | ||
}, []); | ||
|
||
const hideTooltip = useCallback(() => { | ||
setIsTooltipOpen(false); | ||
}, []); | ||
|
||
const onEvents = { | ||
mouseover: updateTooltip, | ||
mouseout: hideTooltip, | ||
}; | ||
|
||
if (!isLoaded) { | ||
return ( | ||
<GitContributionGraphContainer> | ||
<SpinnerWrapper> | ||
<ClipLoader size={'2em'} color="var(--theme-text-subtle)" /> | ||
</SpinnerWrapper> | ||
</GitContributionGraphContainer> | ||
); | ||
} | ||
|
||
return ( | ||
<GitContributionGraphContainer> | ||
<ReactEChartsCore | ||
ref={chartRef} | ||
echarts={echarts} | ||
option={option} | ||
theme={resolvedThemeMode === ThemeMode.Dark ? 'dark' : undefined} | ||
onEvents={onEvents} | ||
style={{ width: '100%', height: '165px' }} | ||
/> | ||
|
||
<BaseTooltip id={tooltipId} isOpen={isTooltipOpen} /> | ||
<span | ||
ref={tooltipRef} | ||
data-tooltip-id={tooltipId} | ||
data-tooltip-content={tooltipContent || ''} | ||
style={{ | ||
position: 'absolute', | ||
// Initially offscreen | ||
left: '-9999px', | ||
top: '-9999px', | ||
pointerEvents: 'none', | ||
}} | ||
/> | ||
</GitContributionGraphContainer> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { GitContributionGraph as default } from './GitContributionGraph'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
'use client'; | ||
|
||
import styled from 'styled-components'; | ||
|
||
export const GitContributionGraphContainer = styled.div` | ||
display: flex; | ||
width: 100%; | ||
height: 165px; | ||
`; | ||
|
||
export const SpinnerWrapper = styled.div` | ||
display: flex; | ||
width: 100%; | ||
justify-content: center; | ||
align-items: center; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export interface GitContributionGraphProps { | ||
data: HeatmapEntry[]; | ||
} | ||
|
||
export type HeatmapEntry = [ | ||
string, // Date in 'YYYY-MM-DD' format | ||
number, // Contribution count | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.