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 {
margin: 0;
.wrapper {
position: relative;
}
.timebox:hover {
background-color: blueviolet;
.line {
position: absolute;
width: 5px;
background-color: var(--secondary-accent);
}
.timeboxHover {
background-color: blue;
.timelineSections {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
gap: 20px;
}
.textbox {
background-color: maroon;
.timelineSection {
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;
box-sizing: border-box;
}
.textboxText {
overflow-y: scroll;
word-break: break-all;
.textHover {
border: 2px solid var(--secondary-accent-light);
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 styles from "./Timeline.module.css";
@ -21,24 +13,14 @@ interface TimelineProps {
width: number;
/** Height of the entire timeline, in pixels. */
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 */
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. */
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. */
textWidth?: number;
/** Height of text label, in pixels. */
textHeight?: number;
/** Distance between labels to middle line, in pixels. */
labelsOffset?: number;
className?: string;
}
@ -46,120 +28,112 @@ export default function Timeline({
data,
width,
height,
padding = 20,
timelinePosition = width / 2,
nodeGap = data.length > 1
? (height - 2 * padding) / (data.length - 1)
: height - 2 * padding,
isTimeUppercase = true,
timeXOffset = -30,
timeWidth = 100,
textXOffset = 30,
textWidth = 100,
textHeight = 100,
timeWidth = 200,
textWidth = 300,
labelsOffset = 50,
className,
}: TimelineProps) {
const dataWithPositions = data.map((data, i) => ({
x: timelinePosition,
y: i * nodeGap + padding,
time: isTimeUppercase ? data.time.toUpperCase() : data.time,
text: data.text,
}));
return (
<svg className={className} width={width} height={height}>
<LinePath
data={dataWithPositions.map((data) => [data.x, data.y])}
stroke="#fff"
strokeWidth={10}
/>
{dataWithPositions.map((data, i) => (
<TimelineAnnotation
positionData={data}
timeXOffset={timeXOffset}
timeWidth={timeWidth}
textXOffset={textXOffset}
textWidth={textWidth}
textHeight={textHeight}
key={`${data.time}${i}`}
/>
))}
</svg>
<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 TimelineAnnotationProps {
positionData: {
x: number;
y: number;
time: string;
text: string;
};
timeXOffset: number;
interface TimelineSectionProps {
datum: TimelineData;
width: number;
isTimeUppercase: boolean;
timeWidth: number;
textXOffset: number;
textWidth: number;
textHeight: number;
labelsOffset: number;
}
function TimelineAnnotation({
positionData,
timeXOffset,
function TimelineSection({
datum,
width,
isTimeUppercase,
timeWidth,
textXOffset,
textWidth,
textHeight,
}: TimelineAnnotationProps) {
const [hover, setHover] = useState(false);
labelsOffset,
}: TimelineSectionProps) {
const [onHover, setHover] = useState(false);
function handleMouseEnter() {
const handleMouseEnter = () => {
setHover(true);
}
console.log(onHover);
};
const handleMouseLeave = () => setHover(false);
function handleMouseLeave() {
setHover(false);
}
console.log(hover);
// divs for customizable margins are necessary, absolute positioning loses vertical flex-box functionality
return (
<Group>
<Annotation x={positionData.x} y={positionData.y} dx={timeXOffset} dy={0}>
<Connector />
<HtmlLabel showAnchorLine={false} verticalAnchor="start">
<h1
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={`${styles.timebox} ${hover ? styles.timeboxHover : ""}`}
style={{ width: timeWidth }}
>
{positionData.time}
</h1>
</HtmlLabel>
</Annotation>
<Annotation x={positionData.x} y={positionData.y} dx={textXOffset} dy={0}>
<Connector />
<CircleSubject
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
radius={10}
fill="#000"
/>
<CircleSubject radius={4} fill="red" />
<HtmlLabel showAnchorLine={false} verticalAnchor="start">
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={styles.textbox}
style={{ width: textWidth, height: textHeight }}
>
<p
className={styles.textboxText}
style={{ maxHeight: textHeight - 10 }}
>
{positionData.text}
</p>
</div>
</HtmlLabel>
</Annotation>
</Group>
<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>
);
}

View File

@ -67,27 +67,27 @@ export const moreMockCategoricalData = [
export const mockTimelineData = [
{
time: "Problem 1",
text: "Overflow-y scroll text not supported by svg",
time: "Fall 2020",
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",
text: "Hover effects don't seem to work with <foreignObject>",
time: "Winter 2021",
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",
text: "Need to add a max-height to these boxes",
time: "Spring 2021",
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",
text: "Styling",
time: "Fall 2021",
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",
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit,",
time: "Winter 2022",
text: "Lorem ipsum dolor sit amet, consectetur adipi",
},
{
time: "Spring 2020",
text: "Lorem ipsum doldolor in repre",
time: "Spring 2022",
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>
<code>{"<Timeline />"}</code>
</h2>
<Timeline data={mockTimelineData} width={400} height={1200} />
<Timeline data={mockTimelineData} width={800} height={1600} />
</div>
);
}