cs-2022-class-profile/components/Timeline.tsx

140 lines
3.4 KiB
TypeScript

import React, { useState } from "react";
import styles from "./Timeline.module.css";
interface TimelineData {
time: string;
text: string;
}
interface TimelineProps {
data: TimelineData[];
/** Width of the entire timeline, in pixels. */
width: number;
/** Height of the entire timeline, in pixels. */
height: number;
/** Whether the time is transformed to uppercase */
isTimeUppercase?: boolean;
/** Width of time label, in pixels. */
timeWidth?: number;
/** Width of text label, in pixels. */
textWidth?: number;
/** Distance between labels to middle line, in pixels. */
labelsOffset?: number;
className?: string;
}
export default function Timeline({
data,
width,
height,
isTimeUppercase = true,
timeWidth = 200,
textWidth = 300,
labelsOffset = 50,
className,
}: TimelineProps) {
return (
<div
className={
className ? `${className} ${styles.wrapper}` : `${styles.wrapper}`
}
style={{ width: width, height: height }}
>
<div
className={styles.line}
style={{ height: height, left: width / 2 }}
></div>
<div className={styles.timelineSections} style={{ width: width }}>
{data.map((datum) => (
<TimelineSection
key={datum.time}
datum={datum}
width={width}
isTimeUppercase={isTimeUppercase}
timeWidth={timeWidth}
textWidth={textWidth}
labelsOffset={labelsOffset}
/>
))}
</div>
</div>
);
}
interface TimelineSectionProps {
datum: TimelineData;
width: number;
isTimeUppercase: boolean;
timeWidth: number;
textWidth: number;
labelsOffset: number;
}
function TimelineSection({
datum,
width,
isTimeUppercase,
timeWidth,
textWidth,
labelsOffset,
}: TimelineSectionProps) {
const [onHover, setHover] = useState(false);
const handleMouseEnter = () => {
setHover(true);
console.log(onHover);
};
const handleMouseLeave = () => setHover(false);
// divs for customizable margins are necessary, absolute positioning loses vertical flex-box functionality
return (
<div className={styles.timelineSection}>
<div
style={{
width: (width - labelsOffset - labelsOffset - 30) / 2 - timeWidth,
}}
></div>
<div
className={onHover ? `${styles.time} ${styles.timeHover}` : styles.time}
style={{
width: timeWidth,
}}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{isTimeUppercase ? datum.time.toUpperCase() : datum.time}
</div>
<div style={{ width: labelsOffset }}></div>
<div
className={styles.circle}
style={{ width: 30 }}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<div
className={styles.innerCircle}
style={{ display: onHover ? "inline" : "none" }}
></div>
</div>
<div style={{ width: labelsOffset }}></div>
<div
className={onHover ? `${styles.text} ${styles.textHover}` : styles.text}
style={{
width: textWidth,
}}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{datum.text}
</div>
<div
style={{
width: (width - labelsOffset - labelsOffset - 30) / 2 - textWidth,
}}
></div>
</div>
);
}