From 34ddb8606758a3712dcd3f446fef88ab432e76b4 Mon Sep 17 00:00:00 2001 From: Michael Neu Date: Wed, 24 Jun 2020 17:37:45 +0200 Subject: [PATCH 1/2] add event graph components --- src/components/event-branch.tsx | 76 ++++++++++++++++++++++++ src/components/event-dot.tsx | 22 +++++++ src/components/event-item.tsx | 39 ++++++++++++ src/components/event-timeline.tsx | 45 ++++++++++++++ src/components/vertical-bezier-curve.tsx | 47 +++++++++++++++ src/constants.ts | 4 ++ 6 files changed, 233 insertions(+) create mode 100644 src/components/event-branch.tsx create mode 100644 src/components/event-dot.tsx create mode 100644 src/components/event-item.tsx create mode 100644 src/components/event-timeline.tsx create mode 100644 src/components/vertical-bezier-curve.tsx diff --git a/src/components/event-branch.tsx b/src/components/event-branch.tsx new file mode 100644 index 0000000..2e3f608 --- /dev/null +++ b/src/components/event-branch.tsx @@ -0,0 +1,76 @@ +import styled from "@emotion/styled"; +import * as React from "react"; +import { eventBranchCurveHeightPixels, eventDotSize } from "../constants"; +import { EventDot } from "./event-dot"; +import { EventTimeline } from "./event-timeline"; +import { VerticalBezierCurve } from "./vertical-bezier-curve"; + +const EventBranchContainer = styled.div` + position: absolute; +`; + +const CurveContainer = styled.div` + position: absolute; +`; + +const OriginContainer = styled.div` + position: absolute; + top: -${eventDotSize}; +`; + +interface IEventBranchProps { + children: React.ReactNode; + offsetX: number; + offsetY: number; + color: string; +} + +export const EventBranch = ({ + children, + offsetX, + offsetY, + color, +}: IEventBranchProps) => { + const referencePointX = offsetX > 0 ? offsetX : 0; + const width = Math.abs(offsetX); + const originPositionProperty = offsetX > 0 ? "left" : "right"; + + return ( + + + + + + + + + + + {children} + + + ); +}; diff --git a/src/components/event-dot.tsx b/src/components/event-dot.tsx new file mode 100644 index 0000000..e18bd95 --- /dev/null +++ b/src/components/event-dot.tsx @@ -0,0 +1,22 @@ +import styled from "@emotion/styled"; +import * as React from "react"; +import { eventDotSize } from "../constants"; + +const Dot = styled.div` + background: currentColor; + width: ${eventDotSize}; + height: ${eventDotSize}; + border-radius: ${eventDotSize}; +`; + +const SpecialDot = styled(Dot)` + background: white; + border: 2px solid currentColor; +`; + +interface IEventDotProps { + isSpecial?: boolean; +} + +export const EventDot = ({ isSpecial = false }: IEventDotProps) => + isSpecial ? : ; diff --git a/src/components/event-item.tsx b/src/components/event-item.tsx new file mode 100644 index 0000000..6eb75c6 --- /dev/null +++ b/src/components/event-item.tsx @@ -0,0 +1,39 @@ +import styled from "@emotion/styled"; +import * as React from "react"; +import { EventDot } from "./event-dot"; + +const EventItemContainer = styled.div` + display: flex; + flex-direction: row; + align-items: baseline; +`; + +const ContentContainer = styled.div` + padding-left: 1rem; +`; + +const Title = styled.h3` + margin: 0; + padding: 0; + white-space: nowrap; +`; + +const ChildrenContainer = styled.div` + padding: 0.5rem 0; + color: white; +`; + +interface IEventItemProps { + children?: React.ReactNode; + title: string; +} + +export const EventItem = ({ children, title }: IEventItemProps) => ( + + + + {title} + {children && {children}} + + +); diff --git a/src/components/event-timeline.tsx b/src/components/event-timeline.tsx new file mode 100644 index 0000000..e530e36 --- /dev/null +++ b/src/components/event-timeline.tsx @@ -0,0 +1,45 @@ +import styled from "@emotion/styled"; +import * as React from "react"; +import { eventDotSize, eventTimelineSize } from "../constants"; +import { EventDot } from "./event-dot"; + +const EventTimelineContainer = styled.div` + position: relative; +`; + +const Line = styled.div` + position: absolute; + left: calc(${eventDotSize} / 2 - ${eventTimelineSize} / 2); + top: 0; + bottom: 0; + + border-left: 2px solid currentColor; + height: 95%; +`; + +const ContentContainer = styled.div` + position: relative; +`; + +const InitialEvent = styled.div` + padding-bottom: 5rem; +`; + +interface IEventTimelineProps { + children: React.ReactNode; + color: string; +} + +export const EventTimeline = ({ children, color }: IEventTimelineProps) => ( + + + + + + + + + {children} + + +); diff --git a/src/components/vertical-bezier-curve.tsx b/src/components/vertical-bezier-curve.tsx new file mode 100644 index 0000000..fbea8ad --- /dev/null +++ b/src/components/vertical-bezier-curve.tsx @@ -0,0 +1,47 @@ +import styled from "@emotion/styled"; +import * as React from "react"; + +const SVG = styled.svg` + width: 100%; + height: 100%; +`; + +const Path = styled.path` + fill: transparent; + stroke: currentColor; +`; + +interface IVerticalBezierCurveProps { + width: number; + height: number; + ratio: number; + strokeWidth: number; + color: string; +} + +export const VerticalBezierCurve = ({ + width, + height, + ratio, + strokeWidth, +}: IVerticalBezierCurveProps) => { + const centerY1 = height / ratio; + const centerY2 = height - height / ratio; + + const isPointingLeft = width < 0; + const normalizedWidth = Math.abs(width); + + const offsetX = strokeWidth / 2; + const left = offsetX; + const right = normalizedWidth - offsetX; + const startX = isPointingLeft ? right : left; + const endX = isPointingLeft ? left : right; + + const pathPoints = `M${startX},0 C${startX},${centerY1} ${endX},${centerY2} ${endX},${height}`; + + return ( + + + + ); +}; diff --git a/src/constants.ts b/src/constants.ts index 4dda0b7..2204362 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -23,3 +23,7 @@ export const asciiArtLogo = ` /_/ \\__,_/\\__/_/____/_.___/\\____/_/ /_/\\__,_/ \\___/\\____/\\__,_/_/_/ /_/\\__, / \\___(_)___(_) /____/ `; + +export const eventDotSize = "12px"; +export const eventTimelineSize = "2px"; +export const eventBranchCurveHeightPixels = 50; From 31beab4951432ac497cd65d1e63901f2f3a0d3ad Mon Sep 17 00:00:00 2001 From: phntxx Date: Wed, 9 Dec 2020 18:59:21 +0100 Subject: [PATCH 2/2] Added Git Graph via gitgraph/react --- package.json | 3 + src/components/event-graph.tsx | 48 +++++++++++ src/components/new-graph.tsx | 149 +++++++++++++++++++++++++++++++++ src/pages/index.tsx | 4 +- yarn.lock | 12 +++ 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/components/event-graph.tsx create mode 100644 src/components/new-graph.tsx diff --git a/package.json b/package.json index 1d3d3aa..6017498 100644 --- a/package.json +++ b/package.json @@ -35,5 +35,8 @@ "rimraf": "^3.0.0", "tslint": "^5.20.1", "typescript": "^3.7.3" + }, + "dependencies": { + "@gitgraph/react": "^1.5.4" } } diff --git a/src/components/event-graph.tsx b/src/components/event-graph.tsx new file mode 100644 index 0000000..ad1e269 --- /dev/null +++ b/src/components/event-graph.tsx @@ -0,0 +1,48 @@ +import styled from "@emotion/styled"; +import * as React from "react"; +import { EventBranch } from "./event-branch"; +import { EventItem } from "./event-item"; +import { EventTimeline } from "./event-timeline"; +const Container = styled.div` + padding: 20vh 0; + width: fit-content; + margin: 0 auto; +`; +const getSpace = (height: number) =>
; +const hackaburgSpace = 10; +const hackaburgSchoolSpace = 5; +const hashcodeSpace = 5; +const colorHashcode = "rgb(255, 99, 132)"; +const colorHackaburg = "#1d74f5"; +const colorHackaburgSchool = "#82bd53"; +const colorStammtisch = "rgb(255, 159, 64)"; +export const EventGraph = () => { + return ( + + + + + {getSpace(hackaburgSchoolSpace)} + + + + + {getSpace(hashcodeSpace)} + + {getSpace(hackaburgSpace)} + + + + + + + {getSpace(hackaburgSpace)} + + {getSpace(hackaburgSpace)} + + {getSpace(hackaburgSpace)} + + + + ); +}; diff --git a/src/components/new-graph.tsx b/src/components/new-graph.tsx new file mode 100644 index 0000000..d0fd077 --- /dev/null +++ b/src/components/new-graph.tsx @@ -0,0 +1,149 @@ +import { Gitgraph, templateExtend, TemplateName } from "@gitgraph/react"; +import styled from "@emotion/styled"; +import * as React from "react"; + +const Container = styled.div` + padding: 20vh 0; + width: fit-content; + margin: 0 auto; +`; + +const GraphContainer = styled.div` + width: 100%; + height: auto; +`; + +const hackaburgSpace = 10; +const hackaburgSchoolSpace = 5; +const hashcodeSpace = 5; +const colorHashcode = "#ff6384"; +const colorHackaburg = "#1d74f5"; +const colorHackaburgSchool = "#82bd53"; +const colorStammtisch = "#ff9f40"; + +const template = templateExtend(TemplateName.Metro, { + branch: { + lineWidth: 5, + label: { + display: false, + }, + }, + commit: { + dot: { + size: 7.5, + }, + message: { + font: "inherit", + displayHash: false, + displayAuthor: false, + }, + }, +}); + +const order = ["hashcode", "hb-stammtisch", "master", "hb-school"]; + +const compareBranchesOrder = (a: string, b: string) => { + return order.indexOf(a) - order.indexOf(b); +}; + +export const EventGraph = () => { + return ( + + + + {(gitgraph) => { + // Simulate git commands with Gitgraph API. + const master = gitgraph.branch({ + name: "master", + + style: { + color: colorHackaburg, + }, + commitDefaultOptions: { + style: { + message: { + color: colorHackaburg, + }, + dot: { + color: colorHackaburg, + }, + }, + }, + }); + master.commit("Hackaburg 2016"); + master.commit("Hackaburg 2017"); + master.commit("Hackaburg 2018"); + + const hashcode = gitgraph.branch({ + name: "hashcode", + style: { + color: colorHashcode, + }, + commitDefaultOptions: { + style: { + message: { + color: colorHashcode, + }, + dot: { + color: colorHashcode, + }, + }, + }, + }); + hashcode.commit("Hashcode 2019"); + + const hackaburgSchool = master.branch({ + name: "hb-school", + style: { + color: colorHackaburgSchool, + }, + commitDefaultOptions: { + style: { + message: { + color: colorHackaburgSchool, + }, + dot: { + color: colorHackaburgSchool, + }, + }, + }, + }); + hackaburgSchool.commit("Hackaburg School 2019"); + + master.commit("Hackaburg 2019"); + + const stammtisch = master.branch({ + name: "hb-stammtisch", + style: { + color: colorStammtisch, + }, + commitDefaultOptions: { + style: { + message: { + color: colorStammtisch, + }, + dot: { + color: colorStammtisch, + }, + }, + }, + }); + stammtisch.commit("Stammtisch"); + + hashcode.commit("Hashcode 2020"); + hackaburgSchool.commit("Hackaburg School 2020"); + + master.commit("Hackaburg 2021"); + }} + + + + ); +}; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 79af8bf..6b33e30 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -3,6 +3,8 @@ import { Page } from "../components/page"; import { Navbar } from "../components/navbar"; import { Anchors } from "../anchors"; +import { EventGraph } from "../components/new-graph"; + export default () => ( ( />
- +
); diff --git a/yarn.lock b/yarn.lock index 24018cc..d63f2e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -956,6 +956,18 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.4.tgz#622a72bebd1e3f48d921563b4b60a762295a81fc" integrity sha512-6PYY5DVdAY1ifaQW6XYTnOMihmBVT27elqSjEoodchsGjzYlEsTQMcEhSud99kVawatyTZRTiVkJ/c6lwbQ7nA== +"@gitgraph/core@1.4.5": + version "1.4.5" + resolved "https://registry.yarnpkg.com/@gitgraph/core/-/core-1.4.5.tgz#4c085413566a58f3b0d8cc22f27c785c354a9034" + integrity sha512-GFXJBfxPyHQOUmKtdlefozSAuQ/4xSg3QWsEJCK+y5YidQVxHWn4wLNUOPXoD7QDScvUOlJq8gOGTsVSpYQGoQ== + +"@gitgraph/react@^1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@gitgraph/react/-/react-1.5.4.tgz#081b799c1a2e6819301a1e1016425170d27caeb0" + integrity sha512-ClknLeAOdzOxbNW18B6ck8U/zrZau+ItD1SjIMT9VRSFe0BtpWrAFE16SR+0zvrc7rF4AK815itPSA/jCllB8w== + dependencies: + "@gitgraph/core" "1.4.5" + "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"