Redo everything
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Jared He 2022-08-21 05:58:48 -04:00
parent 42638cbdb5
commit c1b8ae469f
4 changed files with 174 additions and 142 deletions

View File

@ -1,21 +1,79 @@
.timebox { .wrapper {
margin: 0; position: relative;
} }
.timebox:hover { .line {
background-color: blueviolet; position: absolute;
width: 5px;
background-color: var(--secondary-accent);
} }
.timeboxHover { .timelineSections {
background-color: blue; position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
gap: 20px;
} }
.textbox { .timelineSection {
background-color: maroon; width: 100%;
height: inherit;
display: flex;
flex-direction: row;
justify-content: center;
}
.time {
margin: 0px;
text-align: right;
font-size: 30px;
font-weight: 700;
color: var(--secondary-accent);
word-wrap: break-word;
}
.timeHover {
color: var(--secondary-accent-light);
}
.circle {
margin-left: 2px;
width: 30px;
height: 30px;
border-radius: 30px;
background-color: var(--secondary-accent);
box-shadow: 0px 0px 30px var(--secondary-accent);
display: flex;
justify-content: center;
align-items: center;
}
.innerCircle {
width: 15px;
height: 15px;
border-radius: 15px;
background-color: var(--label);
}
.text {
position: absolute;
margin: 0px;
font-size: 20px;
font-weight: 700;
color: var(--label);
background-color: var(--card-background);
position: relative;
height: fit-content;
word-wrap: break-word;
border-radius: 10px;
padding: 5px; padding: 5px;
box-sizing: border-box;
} }
.textboxText { .textHover {
overflow-y: scroll; border: 2px solid var(--secondary-accent-light);
word-break: break-all; box-shadow: 0px 0px 20px var(--secondary-accent);
} }

View File

@ -1,11 +1,3 @@
import {
Connector,
CircleSubject,
Annotation,
HtmlLabel,
} from "@visx/annotation";
import { Group } from "@visx/group";
import { LinePath } from "@visx/shape";
import React, { useState } from "react"; import React, { useState } from "react";
import styles from "./Timeline.module.css"; import styles from "./Timeline.module.css";
@ -21,24 +13,14 @@ interface TimelineProps {
width: number; width: number;
/** Height of the entire timeline, in pixels. */ /** Height of the entire timeline, in pixels. */
height: number; height: number;
/** Padding top and bottom, in pixels. */
padding: number;
/** Location of timeline width-wise, in pixels. Default is width / 2. */
timelinePosition?: number;
/** Distance between consecutive nodes on timeline, in pixels. Default is evenly spaced. padding is not calculated if user specifies this value. */
nodeGap?: number;
/** Whether the time is transformed to uppercase */ /** Whether the time is transformed to uppercase */
isTimeUppercase?: boolean; isTimeUppercase?: boolean;
/** Offset between the line and the time label, in pixels (negative for left offset, positive for right offset). */
timeXOffset?: number;
/** Width of time label, in pixels. */ /** Width of time label, in pixels. */
timeWidth?: number; timeWidth?: number;
/** Offset between the line and the text label, in pixels (negative for left offset, positive for right offset). */
textXOffset?: number;
/** Width of text label, in pixels. */ /** Width of text label, in pixels. */
textWidth?: number; textWidth?: number;
/** Height of text label, in pixels. */ /** Distance between labels to middle line, in pixels. */
textHeight?: number; labelsOffset?: number;
className?: string; className?: string;
} }
@ -46,120 +28,112 @@ export default function Timeline({
data, data,
width, width,
height, height,
padding = 20,
timelinePosition = width / 2,
nodeGap = data.length > 1
? (height - 2 * padding) / (data.length - 1)
: height - 2 * padding,
isTimeUppercase = true, isTimeUppercase = true,
timeXOffset = -30, timeWidth = 200,
timeWidth = 100, textWidth = 300,
textXOffset = 30, labelsOffset = 50,
textWidth = 100,
textHeight = 100,
className, className,
}: TimelineProps) { }: TimelineProps) {
const dataWithPositions = data.map((data, i) => ({
x: timelinePosition,
y: i * nodeGap + padding,
time: isTimeUppercase ? data.time.toUpperCase() : data.time,
text: data.text,
}));
return ( return (
<svg className={className} width={width} height={height}> <div
<LinePath className={
data={dataWithPositions.map((data) => [data.x, data.y])} className ? `${className} ${styles.wrapper}` : `${styles.wrapper}`
stroke="#fff" }
strokeWidth={10} style={{ width: width, height: height }}
/> >
{dataWithPositions.map((data, i) => ( <div
<TimelineAnnotation className={styles.line}
positionData={data} style={{ height: height, left: width / 2 }}
timeXOffset={timeXOffset} ></div>
timeWidth={timeWidth} <div className={styles.timelineSections} style={{ width: width }}>
textXOffset={textXOffset} {data.map((datum) => (
textWidth={textWidth} <TimelineSection
textHeight={textHeight} key={datum.time}
key={`${data.time}${i}`} datum={datum}
/> width={width}
))} isTimeUppercase={isTimeUppercase}
</svg> timeWidth={timeWidth}
textWidth={textWidth}
labelsOffset={labelsOffset}
/>
))}
</div>
</div>
); );
} }
interface TimelineAnnotationProps { interface TimelineSectionProps {
positionData: { datum: TimelineData;
x: number; width: number;
y: number; isTimeUppercase: boolean;
time: string;
text: string;
};
timeXOffset: number;
timeWidth: number; timeWidth: number;
textXOffset: number;
textWidth: number; textWidth: number;
textHeight: number; labelsOffset: number;
} }
function TimelineAnnotation({ function TimelineSection({
positionData, datum,
timeXOffset, width,
isTimeUppercase,
timeWidth, timeWidth,
textXOffset,
textWidth, textWidth,
textHeight, labelsOffset,
}: TimelineAnnotationProps) { }: TimelineSectionProps) {
const [hover, setHover] = useState(false); const [onHover, setHover] = useState(false);
function handleMouseEnter() { const handleMouseEnter = () => {
setHover(true); setHover(true);
} console.log(onHover);
};
const handleMouseLeave = () => setHover(false);
function handleMouseLeave() { // divs for customizable margins are necessary, absolute positioning loses vertical flex-box functionality
setHover(false);
}
console.log(hover);
return ( return (
<Group> <div className={styles.timelineSection}>
<Annotation x={positionData.x} y={positionData.y} dx={timeXOffset} dy={0}> <div
<Connector /> style={{
<HtmlLabel showAnchorLine={false} verticalAnchor="start"> width: (width - labelsOffset - labelsOffset - 30) / 2 - timeWidth,
<h1 }}
onMouseEnter={handleMouseEnter} ></div>
onMouseLeave={handleMouseLeave} <div
className={`${styles.timebox} ${hover ? styles.timeboxHover : ""}`} className={onHover ? `${styles.time} ${styles.timeHover}` : styles.time}
style={{ width: timeWidth }} style={{
> width: timeWidth,
{positionData.time} }}
</h1> onMouseEnter={handleMouseEnter}
</HtmlLabel> onMouseLeave={handleMouseLeave}
</Annotation> >
<Annotation x={positionData.x} y={positionData.y} dx={textXOffset} dy={0}> {isTimeUppercase ? datum.time.toUpperCase() : datum.time}
<Connector /> </div>
<CircleSubject <div style={{ width: labelsOffset }}></div>
onMouseEnter={handleMouseEnter} <div
onMouseLeave={handleMouseLeave} className={styles.circle}
radius={10} style={{ width: 30 }}
fill="#000" onMouseEnter={handleMouseEnter}
/> onMouseLeave={handleMouseLeave}
<CircleSubject radius={4} fill="red" /> >
<HtmlLabel showAnchorLine={false} verticalAnchor="start"> <div
<div className={styles.innerCircle}
onMouseEnter={handleMouseEnter} style={{ display: onHover ? "inline" : "none" }}
onMouseLeave={handleMouseLeave} ></div>
className={styles.textbox} </div>
style={{ width: textWidth, height: textHeight }} <div style={{ width: labelsOffset }}></div>
> <div
<p className={onHover ? `${styles.text} ${styles.textHover}` : styles.text}
className={styles.textboxText} style={{
style={{ maxHeight: textHeight - 10 }} width: textWidth,
> }}
{positionData.text} onMouseEnter={handleMouseEnter}
</p> onMouseLeave={handleMouseLeave}
</div> >
</HtmlLabel> {datum.text}
</Annotation> </div>
</Group> <div
style={{
width: (width - labelsOffset - labelsOffset - 30) / 2 - textWidth,
}}
></div>
</div>
); );
} }

View File

@ -67,27 +67,27 @@ export const moreMockCategoricalData = [
export const mockTimelineData = [ export const mockTimelineData = [
{ {
time: "Problem 1", time: "Fall 2020",
text: "Overflow-y scroll text not supported by svg", text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
}, },
{ {
time: "Problem 2", time: "Winter 2021",
text: "Hover effects don't seem to work with <foreignObject>", text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad",
}, },
{ {
time: "Problem 3", time: "Spring 2021",
text: "Need to add a max-height to these boxes", text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor i",
}, },
{ {
time: "Problem 4", time: "Fall 2021",
text: "Styling", text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proid",
}, },
{ {
time: "Winter 2020", time: "Winter 2022",
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit,", text: "Lorem ipsum dolor sit amet, consectetur adipi",
}, },
{ {
time: "Spring 2020", time: "Spring 2022",
text: "Lorem ipsum doldolor in repre", text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut en",
}, },
]; ];

View File

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