Code review fixes
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Jared He 2022-08-31 22:34:46 -04:00
parent 3de0f67ccb
commit 4f6f3265fd
3 changed files with 77 additions and 49 deletions

View File

@ -1,15 +1,17 @@
.wrapper { .wrapper {
position: relative; position: relative;
height: 100%;
} }
.line { .line {
position: absolute; position: absolute;
width: calc(5rem / 16); height: 100%;
border-radius: calc(10rem / 16);
background-color: var(--secondary-accent); background-color: var(--secondary-accent);
z-index: -1;
} }
.timelineSections { .timelineSections {
position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
@ -20,7 +22,7 @@
.timelineSection { .timelineSection {
width: 100%; width: 100%;
height: inherit; height: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
@ -35,15 +37,7 @@
word-wrap: break-word; word-wrap: break-word;
} }
.timeHover {
color: var(--secondary-accent-light);
}
.circle { .circle {
margin-left: calc(2rem / 16);
width: calc(30rem / 16);
height: calc(30rem / 16);
border-radius: calc(30rem / 16);
background-color: var(--secondary-accent); background-color: var(--secondary-accent);
box-shadow: calc(0rem / 16) calc(0rem / 16) calc(30rem / 16) var(--secondary-accent); box-shadow: calc(0rem / 16) calc(0rem / 16) calc(30rem / 16) var(--secondary-accent);
display: flex; display: flex;
@ -52,10 +46,8 @@
} }
.innerCircle { .innerCircle {
width: calc(15rem / 16);
height: calc(15rem / 16);
border-radius: calc(15rem / 16);
background-color: var(--label); background-color: var(--label);
display: none;
} }
.text { .text {
@ -66,12 +58,21 @@
font-size: calc(20rem / 16); font-size: calc(20rem / 16);
font-weight: 700; font-weight: 700;
color: var(--label); color: var(--label);
border: calc(2rem / 16) solid var(--card-background);
background-color: var(--card-background); background-color: var(--card-background);
word-wrap: break-word; word-wrap: break-word;
box-sizing: border-box; box-sizing: border-box;
} }
.textHover { .timelineSection:hover .time {
color: var(--secondary-accent-light);
}
.timelineSection:hover .innerCircle {
display: inline;
}
.timelineSection:hover .text {
border: calc(2rem / 16) solid var(--secondary-accent-light); border: calc(2rem / 16) solid var(--secondary-accent-light);
box-shadow: calc(0rem / 16) calc(0rem / 16) calc(20rem / 16) var(--secondary-accent); box-shadow: calc(0rem / 16) calc(0rem / 16) calc(20rem / 16) var(--secondary-accent);
} }

View File

@ -11,50 +11,78 @@ interface TimelineProps {
data: TimelineData[]; data: TimelineData[];
/** Width of the entire timeline, in pixels. */ /** Width of the entire timeline, in pixels. */
width: number; width: number;
/** Height of the entire timeline, in pixels. */ /** Whether the time is transformed to uppercase. */
height: number;
/** Whether the time is transformed to uppercase */
isTimeUppercase?: boolean; isTimeUppercase?: boolean;
/** Width of the middle timeline line, in pixels */
lineWidth?: number;
/** Width of the outer circles on the timeline, in pixels. */
outerCircleWidth?: number;
/** Width of the inner circles on the timeline, in pixels. */
innerCircleWidth?: number;
/** Width of time label, in pixels. */ /** Width of time label, in pixels. */
timeWidth?: number; timeWidth?: number;
/** Width of text label, in pixels. */ /** Width of text label, in pixels. */
textWidth?: number; textWidth?: number;
/** Distance between labels to middle line, in pixels. */ /** Distance between the time label AND the text label to middle line, in pixels. */
labelsOffset?: number; gap?: number;
className?: string; className?: string;
} }
export default function Timeline({ export default function Timeline({
data, data,
width, width,
height,
isTimeUppercase = true, isTimeUppercase = true,
lineWidth = 5,
outerCircleWidth = 30,
innerCircleWidth = 15,
timeWidth = 200, timeWidth = 200,
textWidth = 300, textWidth = 300,
labelsOffset = 50, gap = 50,
className, className,
}: TimelineProps) { }: TimelineProps) {
const largerMiddleElemeent =
outerCircleWidth > lineWidth ? outerCircleWidth : lineWidth;
const requestedWidth =
timeWidth + gap + largerMiddleElemeent + gap + textWidth;
if (requestedWidth > width) {
throw new Error(
`<Timeline /> - timeWidth + gap + ${
outerCircleWidth > lineWidth ? "outerCircleWidth" : "lineWidth"
} + gap + textWidth (${timeWidth} + ${gap} + ${largerMiddleElemeent} + ${gap} + ${textWidth} = ${requestedWidth}) is larger than width (${width})`
);
}
if (innerCircleWidth > outerCircleWidth) {
throw new Error(
`<Timeline /> - innerCircleWidth (${innerCircleWidth}) is larger than outerCircleWidth (${outerCircleWidth})`
);
}
return ( return (
<div <div
className={ className={
className ? `${className} ${styles.wrapper}` : `${styles.wrapper}` className ? `${className} ${styles.wrapper}` : `${styles.wrapper}`
} }
style={{ width: width, height: height }} style={{ width: width }}
> >
<div <div
className={styles.line} className={styles.line}
style={{ height: height, left: width / 2 }} style={{
></div> width: lineWidth,
<div className={styles.timelineSections} style={{ width: width }}> left: width / 2 - lineWidth / 2,
}}
/>
<div className={styles.timelineSections}>
{data.map((datum) => ( {data.map((datum) => (
<TimelineSection <TimelineSection
key={datum.time} key={datum.time}
datum={datum} datum={datum}
width={width} width={width}
isTimeUppercase={isTimeUppercase} isTimeUppercase={isTimeUppercase}
outerCircleWidth={outerCircleWidth}
innerCircleWidth={innerCircleWidth}
timeWidth={timeWidth} timeWidth={timeWidth}
textWidth={textWidth} textWidth={textWidth}
labelsOffset={labelsOffset} gap={gap}
/> />
))} ))}
</div> </div>
@ -66,74 +94,73 @@ interface TimelineSectionProps {
datum: TimelineData; datum: TimelineData;
width: number; width: number;
isTimeUppercase: boolean; isTimeUppercase: boolean;
outerCircleWidth: number;
innerCircleWidth: number;
timeWidth: number; timeWidth: number;
textWidth: number; textWidth: number;
labelsOffset: number; gap: number;
} }
function TimelineSection({ function TimelineSection({
datum, datum,
width, width,
isTimeUppercase, isTimeUppercase,
outerCircleWidth,
innerCircleWidth,
timeWidth, timeWidth,
textWidth, textWidth,
labelsOffset, gap,
}: TimelineSectionProps) { }: TimelineSectionProps) {
const [onHover, setHover] = useState(false); const [onHover, setHover] = useState(false);
const handleMouseEnter = () => { const handleMouseEnter = () => {
setHover(true); setHover(true);
console.log(onHover);
}; };
const handleMouseLeave = () => setHover(false); const handleMouseLeave = () => setHover(false);
// divs for customizable margins are necessary, absolute positioning loses vertical flex-box functionality
return ( return (
<div className={styles.timelineSection}> <div className={styles.timelineSection} style={{ gap: gap }}>
<div
style={{
width: (width - labelsOffset - labelsOffset - 30) / 2 - timeWidth,
}}
></div>
<div <div
className={onHover ? `${styles.time} ${styles.timeHover}` : styles.time} className={onHover ? `${styles.time} ${styles.timeHover}` : styles.time}
style={{ style={{
width: timeWidth, width: timeWidth,
marginLeft: (width - 2 * gap - outerCircleWidth) / 2 - timeWidth,
}} }}
onMouseEnter={handleMouseEnter} onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave} onMouseLeave={handleMouseLeave}
> >
{isTimeUppercase ? datum.time.toUpperCase() : datum.time} {isTimeUppercase ? datum.time.toUpperCase() : datum.time}
</div> </div>
<div style={{ width: labelsOffset }}></div>
<div <div
className={styles.circle} className={styles.circle}
style={{ width: 30 }}
onMouseEnter={handleMouseEnter} onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave} onMouseLeave={handleMouseLeave}
style={{
width: outerCircleWidth,
height: outerCircleWidth,
borderRadius: outerCircleWidth,
}}
> >
<div <div
className={styles.innerCircle} className={styles.innerCircle}
style={{ display: onHover ? "inline" : "none" }} style={{
></div> width: innerCircleWidth,
height: innerCircleWidth,
borderRadius: innerCircleWidth,
}}
/>
</div> </div>
<div style={{ width: labelsOffset }}></div>
<div <div
className={onHover ? `${styles.text} ${styles.textHover}` : styles.text} className={onHover ? `${styles.text} ${styles.textHover}` : styles.text}
style={{ style={{
width: textWidth, width: textWidth,
marginRight: (width - 2 * gap - outerCircleWidth) / 2 - textWidth,
}} }}
onMouseEnter={handleMouseEnter} onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave} onMouseLeave={handleMouseLeave}
> >
{datum.text} {datum.text}
</div> </div>
<div
style={{
width: (width - labelsOffset - labelsOffset - 30) / 2 - textWidth,
}}
></div>
</div> </div>
); );
} }

View File

@ -68,7 +68,7 @@ export default function Home() {
<h2> <h2>
<code>{"<Timeline />"}</code> <code>{"<Timeline />"}</code>
</h2> </h2>
<Timeline data={mockTimelineData} width={800} height={1500} /> <Timeline data={mockTimelineData} width={800} />
</div> </div>
); );
} }