Skip to content

Commit

Permalink
Add tooltips to full history calendar cells
Browse files Browse the repository at this point in the history
  • Loading branch information
O4epegb committed Sep 17, 2024
1 parent c7f99cd commit 7496ccf
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 48 deletions.
115 changes: 72 additions & 43 deletions apps/web/src/screens/Player/Calendar/HeatMap.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import dayjs from 'dayjs'
import { range } from 'lodash-es'
import { Fragment } from 'react'
import { Fragment, useState } from 'react'
import { Tooltip } from '~/components/ui/Tooltip'
import { cn, pluralize, formatNumber } from '~/utils'

Expand Down Expand Up @@ -41,14 +41,7 @@ export const HeatMap = ({
<>
<span className="font-medium">{firstDay.date.format('MMMM YYYY')}</span>
<br />
{games} {pluralize('game', games)}
<br />
{wins} {pluralize('win', wins)}
<br />
{formatNumber((wins / (games || 1)) * 100, {
maximumFractionDigits: 2,
})}
% WR
<StatsTooltipBlock games={games} wins={wins} />
</>
}
>
Expand Down Expand Up @@ -79,14 +72,7 @@ export const HeatMap = ({
{day.date.format('LL')}, {day.date.format('dddd')}
</span>
<br />
{day.games} {pluralize('game', day.games)}
<br />
{day.wins} {pluralize('win', day.wins)}
<br />
{formatNumber((day.wins / (day.games || 1)) * 100, {
maximumFractionDigits: 2,
})}
% WR
<StatsTooltipBlock games={day.games} wins={day.wins} />
</>
}
>
Expand Down Expand Up @@ -123,20 +109,42 @@ export const HeatMap = ({

export const HeatMapFlat = ({
monthesWithDays,
maxGames,
maxGamesPerDay,
invisible,
}: {
monthesWithDays: DayData[][]
maxGames: number
maxGamesPerDay: number
invisible?: boolean
}) => {
const [tooltipData, setTooltipData] = useState<{
ref: HTMLElement
day: DayData
} | null>(null)

return (
<div
className={cn(
'relative grid auto-cols-[16px] grid-flow-col grid-rows-[repeat(7,16px)] items-start justify-start gap-0.5 pt-5',
invisible && 'opacity-0',
)}
>
{tooltipData && (
<Tooltip
restMs={0}
delay={0}
triggerElement={tooltipData.ref}
content={
<>
<span className="font-medium">
{tooltipData.day.date.format('LL')}, {tooltipData.day.date.format('dddd')}
</span>
<br />
<StatsTooltipBlock games={tooltipData.day.games} wins={tooltipData.day.wins} />
</>
}
/>
)}

{monthesWithDays.flatMap((month, monthIndex) => {
const firstDay = month[0]
const games = month.reduce((acc, day) => acc + day.games, 0)
Expand All @@ -157,25 +165,14 @@ export const HeatMapFlat = ({
<div key={i} className="text-center text-transparent" />
))}

<div
className={cn('flex size-full items-center justify-center rounded', {
'border border-zinc-300 dark:border-zinc-400': day.games < maxGames * 0.75,
})}
>
<div className="size-full">
{dayIndex === 0 && (
<Tooltip
content={
<>
<span className="font-medium">{firstDay.date.format('MMMM YYYY')}</span>
<br />
{games} {pluralize('game', games)}
<br />
{wins} {pluralize('win', wins)}
<br />
{formatNumber((wins / (games || 1)) * 100, {
maximumFractionDigits: 2,
})}
% WR
<StatsTooltipBlock games={games} wins={wins} />
</>
}
>
Expand All @@ -185,18 +182,32 @@ export const HeatMapFlat = ({
</Tooltip>
)}
<div
className={cn('rounded', {
'size-[30%]': day.games > 0,
'size-[50%]': day.games >= maxGames * 0.25,
'size-[75%]': day.games >= maxGames * 0.5,
'size-full': day.games >= maxGames * 0.75,
'bg-amber-300': day.winrate === 0,
'bg-emerald-300': day.winrate > 0,
'bg-emerald-400': day.winrate >= 0.1,
'bg-emerald-500': day.winrate >= 0.25,
'bg-emerald-600': day.winrate >= 0.5,
className={cn('flex size-full items-center justify-center rounded', {
'border border-zinc-300 dark:border-zinc-400':
day.games < maxGamesPerDay * 0.75,
})}
/>
onMouseEnter={(e) => {
setTooltipData({
ref: e.currentTarget,
day,
})
}}
onMouseLeave={() => setTooltipData(null)}
>
<div
className={cn('rounded', {
'size-[30%]': day.games > 0,
'size-[50%]': day.games >= maxGamesPerDay * 0.25,
'size-[75%]': day.games >= maxGamesPerDay * 0.5,
'size-full': day.games >= maxGamesPerDay * 0.75,
'bg-amber-300': day.winrate === 0,
'bg-emerald-300': day.winrate > 0,
'bg-emerald-400': day.winrate >= 0.1,
'bg-emerald-500': day.winrate >= 0.25,
'bg-emerald-600': day.winrate >= 0.5,
})}
/>
</div>
</div>
</Fragment>
)
Expand All @@ -205,3 +216,21 @@ export const HeatMapFlat = ({
</div>
)
}

const StatsTooltipBlock = ({ games, wins }: { games: number; wins: number }) => {
return (
<>
<span className="font-medium">{formatNumber(games)}</span> {pluralize('game', games)}
<br />
<span className="font-medium">{wins}</span> {pluralize('win', wins)}
<br />
<span className="font-medium">
{formatNumber((wins / (games || 1)) * 100, {
maximumFractionDigits: 2,
})}
%
</span>{' '}
WR
</>
)
}
34 changes: 29 additions & 5 deletions apps/web/src/screens/Player/Calendar/HistoryDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { api } from '~/api'
import { Dialog, DialogContent, DialogTrigger } from '~/components/ui/Dialog'
import { Loader } from '~/components/ui/Loader'
import { usePlayerPageContext } from '~/screens/Player/context'
import { date } from '~/utils'
import { date, formatNumber } from '~/utils'
import { DayData, HeatMapFlat } from './HeatMap'

export const HistoryDialog = () => {
Expand Down Expand Up @@ -88,19 +88,43 @@ const Content = () => {
.reverse()
}, [calendarData])

const maxGamesPerDay =
Math.max(
...yearlyData.flatMap((yearData) => yearData.monthesWithDays.flat().map((day) => day.games)),
) || 1

return (
<>
<div className="flex items-center gap-2">
{isLoading && <Loader />}
{error && <div className="text-sm text-red-600">Error fetching data</div>}
</div>

<div className="space-y-3">
<div className="space-y-4">
{yearlyData.map((yearData, yearIndex) => {
const games = yearData.monthesWithDays.flat().reduce((acc, day) => acc + day.games, 0)
const wins = yearData.monthesWithDays.flat().reduce((acc, day) => acc + day.wins, 0)

return (
<div key={yearIndex}>
<h3 className="text-sm font-bold">{yearData.year}</h3>
<HeatMapFlat maxGames={50} monthesWithDays={yearData.monthesWithDays} />
<div key={yearIndex} className="space-y-0.5">
<div className="flex gap-2">
<h3 className="text-sm font-bold">{yearData.year}</h3>
<span className="inline-flex items-center gap-1">
<span className="text-xs">Games: {games}</span>
<span className="text-xs">Wins: {wins}</span>
<span className="text-xs">
Winrate:{' '}
{formatNumber((wins / (games || 1)) * 100, {
maximumFractionDigits: 2,
})}
%
</span>
</span>
</div>
<HeatMapFlat
maxGamesPerDay={maxGamesPerDay}
monthesWithDays={yearData.monthesWithDays}
/>
</div>
)
})}
Expand Down

0 comments on commit 7496ccf

Please sign in to comment.