-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
9,769 additions
and
29,284 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
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,68 @@ | ||
import React from 'react'; | ||
import { Card, CardContent, Typography, Box, IconButton, CardActionArea, Tooltip, Grid, useTheme } from '@mui/material'; | ||
import { Alarm, AccessTime, ArrowForward } from '@mui/icons-material'; | ||
import ContestTimer from './ContestTimer'; | ||
import { ColorName, Colors } from '~/lib/ratings'; | ||
|
||
interface ContestCardProps { | ||
contestName: string; | ||
contestScreenName: string; | ||
startTime: Date; | ||
endTime: Date; | ||
contestType: 'algorithm' | 'heuristic'; | ||
contestColor: ColorName; | ||
} | ||
|
||
function capitalize(s: string) { | ||
return s.charAt(0).toUpperCase() + s.slice(1); | ||
} | ||
|
||
export default (({ contestName, contestScreenName, startTime, endTime, contestType, contestColor }) => { | ||
const theme = useTheme(); | ||
|
||
return ( | ||
<Card sx={{ marginBottom: 2, borderRadius: '8px', border: '1px solid #ccc', backgroundColor: Colors[contestColor][theme.palette.mode].background }}> | ||
<CardActionArea href={`/contests/${contestScreenName}`}> | ||
<CardContent> | ||
<Grid container spacing={2} justifyContent="center" alignItems="center"> | ||
<Grid item xs={1}> | ||
<Tooltip title={capitalize(contestType)}> | ||
<Box sx={{ | ||
display: 'flex', | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
width: 40, | ||
height: 40, | ||
borderRadius: '50%', | ||
backgroundColor: theme.palette.background.paper, | ||
marginRight: 2, | ||
border: '1px solid #ccc', | ||
fontSize: '1rem', | ||
fontWeight: 'bold', | ||
}}> | ||
{contestType[0].toUpperCase()} | ||
</Box> | ||
</Tooltip> | ||
</Grid> | ||
<Grid item xs={8}> | ||
<Typography variant="h6">{contestName}</Typography> | ||
</Grid> | ||
<Grid item xs={2}> | ||
<ContestTimer | ||
startDate={startTime} | ||
endDate={endTime} | ||
contentOnOver='Contest is over!' | ||
timeDeltaProps={{ | ||
color: theme.palette.mode === 'dark' ? 'primary' : 'secondary', | ||
}} | ||
/> | ||
</Grid> | ||
<Grid item xs={1}> | ||
<ArrowForward /> | ||
</Grid> | ||
</Grid> | ||
</CardContent> | ||
</CardActionArea> | ||
</Card> | ||
); | ||
}) satisfies React.FC<ContestCardProps>; |
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,66 @@ | ||
import React, { useEffect, useState } from "react"; | ||
import TimeDelta from "./datetime/timeDelta"; | ||
import type { SvgIconProps, TypographyProps } from "@mui/material"; | ||
import { Stack, Typography } from "@mui/material"; | ||
import { AvTimer, HourglassTop } from "@mui/icons-material"; | ||
|
||
const ContestTimer: React.FC< | ||
{ | ||
startDate: string | number | Date; | ||
endDate: string | number | Date; | ||
contentOnOver: string; | ||
iconProps?: SvgIconProps; | ||
timeDeltaProps?: Omit<React.ComponentProps<typeof TimeDelta>, "msec" | "innerComponent">; | ||
} & Omit<TypographyProps, "key"> | ||
> = ({ startDate, endDate, contentOnOver, iconProps, timeDeltaProps, ...other }) => { | ||
const [now, setNow] = useState(Date.now()); | ||
|
||
useEffect(() => { | ||
const update = () => setNow(Date.now()); | ||
update(); | ||
const id = setInterval(update, 1000); | ||
return () => clearInterval(id); | ||
}, []); | ||
|
||
const startTime = new Date(startDate).getTime(); | ||
const endTime = new Date(endDate).getTime(); | ||
|
||
const InnerComponent: React.FC<{ | ||
value: string; | ||
unit: string; | ||
isFirst: boolean; | ||
isLast: boolean; | ||
}> = ({ value, unit, isFirst, isLast }) => { | ||
return ( | ||
<Stack direction="row" alignItems="baseline"> | ||
<Typography {...other} textAlign="right" suppressHydrationWarning> | ||
{value} | ||
</Typography> | ||
<Typography {...other} fontSize="95%"> | ||
{unit} | ||
</Typography> | ||
</Stack> | ||
); | ||
}; | ||
|
||
if (now < endTime) { | ||
const [anchorTime, Icon] = | ||
now < startTime ? [startTime, HourglassTop] : [endTime, AvTimer]; | ||
return ( | ||
<Stack direction="row" justifyContent="flex-end"> | ||
<Icon {...iconProps} /> | ||
<TimeDelta | ||
msec={anchorTime - now} | ||
gap={0.9} | ||
innerComponent={InnerComponent} | ||
minWidth={130} | ||
{...timeDeltaProps} | ||
/> | ||
</Stack> | ||
); | ||
} else { | ||
return <>{contentOnOver}</>; | ||
} | ||
}; | ||
|
||
export default ContestTimer; |
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,15 @@ | ||
import React from 'react'; | ||
import { Typography } from '@mui/material'; | ||
|
||
export function ColorizedRatingText({ rating }: { rating: number }): JSX.Element { | ||
let color = ''; | ||
if (rating >= 4) { | ||
color = 'green'; | ||
} else if (rating >= 2) { | ||
color = 'orange'; | ||
} else { | ||
color = 'red'; | ||
} | ||
|
||
return <Typography style={{ color }}>{rating}</Typography>; | ||
} |
Empty file.
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 @@ | ||
import { NoSsr } from "@mui/material"; | ||
import { formatInTimeZone } from "date-fns-tz"; | ||
import React from "react"; | ||
import { getLocalTimezone } from "~/lib/util"; | ||
|
||
const LocalDate: React.FC<{ | ||
date: string | number | Date; | ||
formatStr?: string; | ||
}> = ({ date, formatStr = "yyyy-MM-dd HH:mm zzz" }) => { | ||
const format = (timezone: string) => | ||
formatInTimeZone(date, timezone, formatStr); | ||
|
||
return <NoSsr fallback={format("UTC")}>{format(getLocalTimezone())}</NoSsr>; | ||
}; | ||
|
||
export default LocalDate; |
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,35 @@ | ||
import { NoSsr } from "@mui/material"; | ||
import { formatInTimeZone } from "date-fns-tz"; | ||
import React from "react"; | ||
import { getLocalTimezone } from "~/lib/util"; | ||
|
||
const LocalDateRange: React.FC<{ | ||
startDate: string | number | Date; | ||
endDate: string | number | Date; | ||
dateFormatStr?: string; | ||
timeFormatStr?: string; | ||
timeZoneFormatStr?: string; | ||
}> = ({ | ||
startDate, | ||
endDate, | ||
dateFormatStr = "yyyy-MM-dd", | ||
timeFormatStr = "HH:mm", | ||
timeZoneFormatStr = "zzz", | ||
}) => { | ||
const format = (timezone: string) => { | ||
const startDateStr = formatInTimeZone(startDate, timezone, dateFormatStr); | ||
const startTimeStr = formatInTimeZone(startDate, timezone, timeFormatStr); | ||
const endDateStr = formatInTimeZone(endDate, timezone, dateFormatStr); | ||
const endTimeStr = formatInTimeZone(endDate, timezone, timeFormatStr); | ||
const tzStr = formatInTimeZone(startDate, timezone, timeZoneFormatStr); | ||
if (startDateStr === endDateStr) { | ||
return `${startDateStr} ${startTimeStr} - ${endTimeStr} ${tzStr}`; | ||
} else { | ||
return `${startDateStr} ${startTimeStr} - ${endDateStr} ${endTimeStr} ${tzStr}`; | ||
} | ||
}; | ||
|
||
return <NoSsr fallback={format("UTC")}>{format(getLocalTimezone())}</NoSsr>; | ||
}; | ||
|
||
export default LocalDateRange; |
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,30 @@ | ||
import { Box, NoSsr, TextField, Typography } from "@mui/material"; | ||
import { formatInTimeZone } from "date-fns-tz"; | ||
import React, { ComponentProps, useState } from "react"; | ||
import { getLocalTimezone } from "~/lib/util"; | ||
|
||
const LocalDateTimeField: React.FC< | ||
Omit<ComponentProps<typeof TextField>, "type" | "defaultValue"> & { defaultValue?: Date } | ||
> = props => { | ||
const { defaultValue, name, onChange, ...rest } = props; | ||
|
||
const format = (timezone: string) => | ||
defaultValue && formatInTimeZone(defaultValue, timezone, "yyyy-MM-dd@HH:mm:ss").replace("@", "T"); | ||
|
||
const [isoString, setISOString] = useState<string | undefined>(defaultValue && defaultValue.toISOString()); | ||
|
||
return <NoSsr fallback={<Box><Typography>loading...</Typography></Box>}> | ||
<TextField | ||
type="datetime-local" | ||
defaultValue={format(getLocalTimezone())} | ||
onChange={event => { | ||
setISOString(new Date(event.target.value).toISOString()); | ||
if (onChange) onChange(event); | ||
}} | ||
{...rest} | ||
/> | ||
<input hidden={true} type="string" name={name} readOnly value={isoString} /> | ||
</NoSsr>; | ||
}; | ||
|
||
export default LocalDateTimeField; |
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,118 @@ | ||
import type { StackProps } from "@mui/material"; | ||
import { Stack, Typography } from "@mui/material"; | ||
import React from "react"; | ||
|
||
const units = [ | ||
{ base: 1000, unit: ["ms", "msecs", "milliseconds"], maxDigit: 3 }, | ||
{ base: 60, unit: ["s", "secs", "seconds"], maxDigit: 2 }, | ||
{ base: 60, unit: ["m", "mins", "minutes"], maxDigit: 2 }, | ||
{ base: 24, unit: ["h", "hours", "hours"], maxDigit: 2 }, | ||
{ base: Infinity, unit: ["d", "days", "days"], maxDigit: 0 }, | ||
]; | ||
|
||
const defaultInnerComponent: React.FC<{ | ||
value: string; | ||
unit: string; | ||
isFirst: boolean; | ||
isLast: boolean; | ||
}> = ({ value, unit, isFirst, isLast }) => { | ||
return ( | ||
<> | ||
<Typography variant="inherit" component="span"> | ||
{value} | ||
</Typography> | ||
{isLast ? null : ( | ||
<Typography variant="inherit" component="span"> | ||
: | ||
</Typography> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
const TimeDelta: React.FC< | ||
{ | ||
msec: number; | ||
precision?: "ms" | "seconds" | "minutes" | "hours" | "days" | number; | ||
headMinUnit?: "ms" | "seconds" | "minutes" | "hours" | "days"; | ||
headMaxUnit?: "ms" | "seconds" | "minutes" | "hours" | "days"; | ||
unitType?: "shortest" | "short" | "long"; | ||
zeroFill?: boolean; | ||
fillHead?: boolean; // true: 01h 02m 03s, false: 1h 02m 03s | ||
gap?: string | number; | ||
|
||
innerComponent?: React.FC<{ | ||
value: string; | ||
unit: string; | ||
isFirst: boolean; | ||
isLast: boolean; | ||
}>; | ||
} & StackProps | ||
> = ({ | ||
msec, | ||
precision = "seconds", | ||
headMinUnit = "ms", | ||
headMaxUnit = "days", | ||
unitType = "shortest", | ||
zeroFill = true, | ||
fillHead = false, | ||
gap = "1px", | ||
innerComponent: Component = defaultInnerComponent, | ||
...other | ||
}) => { | ||
const isNegative = msec < 0; | ||
const rounded = Math.round(Math.abs(msec)); | ||
|
||
const unitIndex = ["shortest", "short", "long"].indexOf(unitType); | ||
|
||
const result = []; | ||
|
||
let remain = rounded; | ||
let display = false; | ||
let minUnitAchieved = false; | ||
for (let i = 0; i < units.length; i++) { | ||
const { base, unit, maxDigit } = units[i]; | ||
display ||= typeof(precision) == "string" ? unit.includes(precision) : true; | ||
minUnitAchieved ||= unit.includes(headMinUnit); | ||
|
||
const current = remain % base; | ||
remain = (remain - current) / base; | ||
|
||
if (!display) continue; | ||
|
||
const isHead = | ||
unit.includes(headMaxUnit) || (remain === 0 && minUnitAchieved); | ||
const isTail = result.length == 0; | ||
|
||
let currentStr = current.toString(); | ||
if (zeroFill && (!isHead || fillHead)) { | ||
currentStr = currentStr.padStart(maxDigit, "0"); | ||
} | ||
if (isHead && isNegative) { | ||
currentStr = "-" + currentStr; | ||
} | ||
const unitStr = unit[unitIndex]; | ||
|
||
result.unshift( | ||
<Component | ||
key={unitStr} | ||
value={currentStr} | ||
unit={unitStr} | ||
isFirst={isHead} | ||
isLast={isTail} | ||
/> | ||
); | ||
if (isHead) break; | ||
} | ||
if (typeof(precision) == "number") { | ||
while (precision < result.length) result.pop(); | ||
} | ||
|
||
return ( | ||
<Stack direction="row" alignItems="baseline" justifyContent="flex-end" gap={gap} {...other}> | ||
{result} | ||
</Stack> | ||
); | ||
}; | ||
|
||
export default TimeDelta; |
Oops, something went wrong.