diff --git a/components/Timeline.module.css b/components/Timeline.module.css new file mode 100644 index 0000000..0f3c12c --- /dev/null +++ b/components/Timeline.module.css @@ -0,0 +1,21 @@ +.timebox { + margin: 0; +} + +.timebox:hover { + background-color: blueviolet; +} + +.timeboxHover { + background-color: blue; +} + +.textbox { + background-color: maroon; + padding: 5px; +} + +.textboxText { + overflow-y: scroll; + word-break: break-all; +} \ No newline at end of file diff --git a/components/Timeline.tsx b/components/Timeline.tsx index 1b37c64..6cc1d09 100644 --- a/components/Timeline.tsx +++ b/components/Timeline.tsx @@ -1,4 +1,14 @@ -import React from "react"; +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"; interface TimelineData { time: string; @@ -7,21 +17,149 @@ interface TimelineData { interface TimelineProps { data: TimelineData[]; - /** Distance between consecutive nodes on timeline, in pixels */ + /** Width of the entire timeline, in pixels. */ + 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; className?: string; } export default function Timeline({ data, - nodeGap = 40, + 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, + className, }: TimelineProps) { - return
; + const dataWithPositions = data.map((data, i) => ({ + x: timelinePosition, + y: i * nodeGap + padding, + time: isTimeUppercase ? data.time.toUpperCase() : data.time, + text: data.text, + })); + + return ( + + [data.x, data.y])} + stroke="#fff" + strokeWidth={10} + /> + {dataWithPositions.map((data, i) => ( + + ))} + + ); } -function TimelineSection() { - return
; +interface TimelineAnnotationProps { + positionData: { + x: number; + y: number; + time: string; + text: string; + }; + timeXOffset: number; + timeWidth: number; + textXOffset: number; + textWidth: number; + textHeight: number; +} + +function TimelineAnnotation({ + positionData, + timeXOffset, + timeWidth, + textXOffset, + textWidth, + textHeight, +}: TimelineAnnotationProps) { + const [hover, setHover] = useState(false); + + function handleMouseEnter() { + setHover(true); + } + + function handleMouseLeave() { + setHover(false); + } + console.log(hover); + return ( + + + + +

+ {positionData.time} +

+
+
+ + + + + +
+

+ {positionData.text} +

+
+
+
+
+ ); } diff --git a/data/mocks.ts b/data/mocks.ts index 971363a..3fdb9cc 100644 --- a/data/mocks.ts +++ b/data/mocks.ts @@ -67,20 +67,20 @@ export const moreMockCategoricalData = [ export const mockTimelineData = [ { - time: "Fall 2018", - 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 1", + text: "Overflow-y scroll text not supported by svg", }, { - time: "Winter 2019", - text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia", + time: "Problem 2", + text: "Hover effects don't seem to work with ", }, { - time: "Spring 2019", - 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 volupta", + time: "Problem 3", + text: "Need to add a max-height to these boxes", }, { - 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. 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 4", + text: "Styling", }, { time: "Winter 2020", @@ -88,6 +88,6 @@ export const mockTimelineData = [ }, { time: "Spring 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 repre", + text: "Lorem ipsum doldolor in repre", }, ]; diff --git a/package-lock.json b/package-lock.json index 7f2a1f1..f9a2aec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "packages": { "": { - "name": "cs-2022-class-profile", "version": "0.1.0", "dependencies": { + "@visx/annotation": "^2.12.2", "@visx/axis": "^2.10.0", "@visx/event": "^2.6.0", "@visx/grid": "^2.10.0", @@ -886,6 +886,63 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@visx/annotation": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@visx/annotation/-/annotation-2.12.2.tgz", + "integrity": "sha512-NhIexNL2QJKc5escOpCe5apNdqBUqmQzGLqc40L7GslYuS3KgxtMa4tdpI+WCct8b/EK3fyQmN9oqG0H5HTY9A==", + "dependencies": { + "@types/react": "*", + "@visx/drag": "2.10.0", + "@visx/group": "2.10.0", + "@visx/point": "2.6.0", + "@visx/shape": "2.12.2", + "@visx/text": "2.12.2", + "classnames": "^2.3.1", + "prop-types": "^15.5.10", + "react-use-measure": "^2.0.4" + }, + "peerDependencies": { + "react": "^16.0.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, + "node_modules/@visx/annotation/node_modules/@visx/shape": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@visx/shape/-/shape-2.12.2.tgz", + "integrity": "sha512-4gN0fyHWYXiJ+Ck8VAazXX0i8TOnLJvOc5jZBnaJDVxgnSIfCjJn0+Nsy96l9Dy/bCMTh4DBYUBv9k+YICBUOA==", + "dependencies": { + "@types/d3-path": "^1.0.8", + "@types/d3-shape": "^1.3.1", + "@types/lodash": "^4.14.172", + "@types/react": "*", + "@visx/curve": "2.1.0", + "@visx/group": "2.10.0", + "@visx/scale": "2.2.2", + "classnames": "^2.3.1", + "d3-path": "^1.0.5", + "d3-shape": "^1.2.0", + "lodash": "^4.17.21", + "prop-types": "^15.5.10" + }, + "peerDependencies": { + "react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, + "node_modules/@visx/annotation/node_modules/@visx/text": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@visx/text/-/text-2.12.2.tgz", + "integrity": "sha512-Sv9YEolggfv2Nf6+l28ESG3VXVR1+s4u/Cz17QpgOxygcbOM8LfLtriWtBsBMKdMbYKeUpoUro0clx55TUwzew==", + "dependencies": { + "@types/lodash": "^4.14.172", + "@types/react": "*", + "classnames": "^2.3.1", + "lodash": "^4.17.21", + "prop-types": "^15.7.2", + "reduce-css-calc": "^1.3.0" + }, + "peerDependencies": { + "react": "^16.3.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, "node_modules/@visx/axis": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@visx/axis/-/axis-2.10.0.tgz", @@ -927,6 +984,20 @@ "d3-shape": "^1.0.6" } }, + "node_modules/@visx/drag": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@visx/drag/-/drag-2.10.0.tgz", + "integrity": "sha512-1G7ABfue8/Jn7tHxEPsDK+84Jbgej3Cqgi8FHaV15XlDRlaWs/fDNz4ECdJUGvhXuXLYCpaWFzhD1HaSEDJL1g==", + "dependencies": { + "@types/react": "*", + "@visx/event": "2.6.0", + "@visx/point": "2.6.0", + "prop-types": "^15.5.10" + }, + "peerDependencies": { + "react": "^16.8.0-0 || ^17.0.0-0 || ^18.0.0-0" + } + }, "node_modules/@visx/event": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@visx/event/-/event-2.6.0.tgz", @@ -4975,6 +5046,56 @@ "eslint-visitor-keys": "^3.3.0" } }, + "@visx/annotation": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@visx/annotation/-/annotation-2.12.2.tgz", + "integrity": "sha512-NhIexNL2QJKc5escOpCe5apNdqBUqmQzGLqc40L7GslYuS3KgxtMa4tdpI+WCct8b/EK3fyQmN9oqG0H5HTY9A==", + "requires": { + "@types/react": "*", + "@visx/drag": "2.10.0", + "@visx/group": "2.10.0", + "@visx/point": "2.6.0", + "@visx/shape": "2.12.2", + "@visx/text": "2.12.2", + "classnames": "^2.3.1", + "prop-types": "^15.5.10", + "react-use-measure": "^2.0.4" + }, + "dependencies": { + "@visx/shape": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@visx/shape/-/shape-2.12.2.tgz", + "integrity": "sha512-4gN0fyHWYXiJ+Ck8VAazXX0i8TOnLJvOc5jZBnaJDVxgnSIfCjJn0+Nsy96l9Dy/bCMTh4DBYUBv9k+YICBUOA==", + "requires": { + "@types/d3-path": "^1.0.8", + "@types/d3-shape": "^1.3.1", + "@types/lodash": "^4.14.172", + "@types/react": "*", + "@visx/curve": "2.1.0", + "@visx/group": "2.10.0", + "@visx/scale": "2.2.2", + "classnames": "^2.3.1", + "d3-path": "^1.0.5", + "d3-shape": "^1.2.0", + "lodash": "^4.17.21", + "prop-types": "^15.5.10" + } + }, + "@visx/text": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@visx/text/-/text-2.12.2.tgz", + "integrity": "sha512-Sv9YEolggfv2Nf6+l28ESG3VXVR1+s4u/Cz17QpgOxygcbOM8LfLtriWtBsBMKdMbYKeUpoUro0clx55TUwzew==", + "requires": { + "@types/lodash": "^4.14.172", + "@types/react": "*", + "classnames": "^2.3.1", + "lodash": "^4.17.21", + "prop-types": "^15.7.2", + "reduce-css-calc": "^1.3.0" + } + } + } + }, "@visx/axis": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@visx/axis/-/axis-2.10.0.tgz", @@ -5009,6 +5130,17 @@ "d3-shape": "^1.0.6" } }, + "@visx/drag": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@visx/drag/-/drag-2.10.0.tgz", + "integrity": "sha512-1G7ABfue8/Jn7tHxEPsDK+84Jbgej3Cqgi8FHaV15XlDRlaWs/fDNz4ECdJUGvhXuXLYCpaWFzhD1HaSEDJL1g==", + "requires": { + "@types/react": "*", + "@visx/event": "2.6.0", + "@visx/point": "2.6.0", + "prop-types": "^15.5.10" + } + }, "@visx/event": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@visx/event/-/event-2.6.0.tgz", diff --git a/package.json b/package.json index 6358840..4dc64b2 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,16 @@ "lint:fix": "eslint \"{pages,components}/**/*.{js,ts,tsx,jsx}\" --quiet --fix" }, "dependencies": { + "@visx/annotation": "^2.12.2", "@visx/axis": "^2.10.0", "@visx/event": "^2.6.0", "@visx/grid": "^2.10.0", "@visx/group": "^2.10.0", "@visx/scale": "^2.2.2", "@visx/shape": "^2.10.0", + "@visx/text": "^2.10.0", "@visx/tooltip": "^2.10.0", "@visx/wordcloud": "^2.10.0", - "@visx/text": "^2.10.0", "next": "12.1.6", "react": "18.1.0", "react-dom": "18.1.0" diff --git a/pages/playground.tsx b/pages/playground.tsx index 58197f9..0563c88 100644 --- a/pages/playground.tsx +++ b/pages/playground.tsx @@ -1,8 +1,13 @@ -import Timeline from "@/components/Timeline"; import { BarGraphHorizontal, BarGraphVertical } from "components/BarGraph"; -import { mockCategoricalData, moreMockCategoricalData } from "data/mocks"; +import { + mockCategoricalData, + moreMockCategoricalData, + mockTimelineData, +} from "data/mocks"; import React from "react"; +import Timeline from "@/components/Timeline"; + import { ColorPalette } from "../components/ColorPalette"; import { WordCloud } from "../components/WordCloud"; @@ -60,7 +65,10 @@ export default function Home() { value: word.value, }))} /> - +

+ {""} +

+ ); }