Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(view): add transition in ClusterGraph #142

Merged
merged 4 commits into from
Sep 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const NODE_GAP = 20;
export const COMMIT_HEIGHT = 50;
export const CLUSTER_HEIGHT = 50;
export const GRAPH_WIDTH = 100;
export const SVG_WIDTH = GRAPH_WIDTH + 4;
export const DETAIL_HEIGHT = 300;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
.cluster-container {
.cluster-box {
.cluster-graph_container {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변수 naming부분은 이전 리뷰들을 보니까 view팀에서 정의하신 부분들이 있는 것 같아요.
@githru/view 팀분들 확인부탁드립니다~.

.cluster-graph_cluster {
rx: 5;
stroke-width: 1;
stroke: rgb(13, 71, 161, 0.4);
fill: transparent;
}

.degree-box {
.cluster-graph_degree {
rx: 5;
fill: rgb(13, 71, 161, 0.4);
}

&:hover {
.cluster-box {
.cluster-graph_cluster {
stroke-width: 3;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { MouseEvent, RefObject } from "react";
import type { RefObject } from "react";
import React, { useEffect, useRef } from "react";
import * as d3 from "d3";

Expand All @@ -8,13 +8,16 @@ import "./ClusterGraph.scss";

import { selectedDataUpdater } from "../VerticalClusterList.util";

import { getGraphHeight, getClusterSizes } from "./ClusterGraph.util";
import {
COMMIT_HEIGHT,
getGraphHeight,
getClusterSizes,
getSelectedIndex,
getClusterPosition,
} from "./ClusterGraph.util";
import {
CLUSTER_HEIGHT,
DETAIL_HEIGHT,
GRAPH_WIDTH,
NODE_GAP,
SVG_MARGIN,
SVG_WIDTH,
} from "./ClusterGraph.const";
import type {
Expand All @@ -25,66 +28,43 @@ import type {
const drawClusterBox = (container: SVGElementSelection<SVGGElement>) => {
container
.append("rect")
.attr("class", "cluster-box ")
.attr("class", "cluster-graph_cluster")
.attr("width", GRAPH_WIDTH)
.attr("height", COMMIT_HEIGHT)
.attr("x", SVG_MARGIN.left)
.attr("y", (d, i, prev) =>
i === 0
? SVG_MARGIN.top
: prev[i - 1].y.baseVal.value +
prev[i - 1].height.baseVal.value +
NODE_GAP +
(d.selected === d.cluster.commitNodeList[0].clusterId
? DETAIL_HEIGHT
: 0)
);
.attr("height", CLUSTER_HEIGHT);
};

const drawDegreeBox = (container: SVGElementSelection<SVGGElement>) => {
const widthScale = d3.scaleLinear().range([0, GRAPH_WIDTH]).domain([0, 10]);
container
.append("rect")
.attr("class", "degree-box")
.attr("class", "cluster-graph_degree")
.attr("width", (d) => widthScale(Math.min(d.clusterSize, 10)))
.attr("height", COMMIT_HEIGHT)
.attr("height", CLUSTER_HEIGHT)
.attr(
"x",
(d) =>
SVG_MARGIN.left +
GRAPH_WIDTH / 2 -
widthScale(Math.min(d.clusterSize, 10)) / 2
)
.attr("y", (d, i, prev) =>
i === 0
? SVG_MARGIN.top
: prev[i - 1].y.baseVal.value +
prev[i - 1].height.baseVal.value +
NODE_GAP +
(d.selected === d.cluster.commitNodeList[0].clusterId
? DETAIL_HEIGHT
: 0)
(d) => (GRAPH_WIDTH - widthScale(Math.min(d.clusterSize, 10))) / 2
);
};

const drawClusterGraph = (
svgRef: RefObject<SVGSVGElement>,
data: ClusterGraphElement[],
onClickCluster: (
this: SVGGElement,
event: MouseEvent,
d: ClusterGraphElement
) => void
onClickCluster: (_: PointerEvent, d: ClusterGraphElement) => void
) => {
console.log(data);
const group = d3
.select(svgRef.current)
.selectAll(".cluster-container")
.selectAll(".cluster-graph_container")
.data(data)
.enter()
.append("g")
.attr("class", "cluster-container")
.on("click", onClickCluster);
.join("g")
.on("click", onClickCluster)
.attr("class", "cluster-graph_container")
.attr("transform", (d, i) => getClusterPosition(d, i, true));
group
.transition()
.duration(300)
.ease(d3.easeLinear)
.attr("transform", (d, i) => getClusterPosition(d, i));

drawClusterBox(group);
drawDegreeBox(group);
};
Expand All @@ -98,42 +78,34 @@ type ClusterGraphProps = {
setSelectedData: React.Dispatch<React.SetStateAction<SelectedDataProps>>;
};

const getSelectedNextId = (
data: ClusterNode[],
selectedData: SelectedDataProps
) => {
const selectedId = selectedData?.commitNodeList[0].clusterId;
const selectedNextId =
data.findIndex((item) => item.commitNodeList[0].clusterId === selectedId) +
1;
return data[selectedNextId]?.commitNodeList[0]?.clusterId;
};
const ClusterGraph = ({
data,
selectedData,
setSelectedData,
}: ClusterGraphProps) => {
const svgRef = useRef<SVGSVGElement>(null);
const clusterSizes = getClusterSizes(data);
const graphHeight = getGraphHeight(clusterSizes);
const selectedNextId = getSelectedNextId(data, selectedData);
const selectedIndex = getSelectedIndex(data, selectedData);
const graphHeight =
getGraphHeight(clusterSizes) + (selectedIndex < 0 ? 0 : DETAIL_HEIGHT);

const clusterGraphElements = data.map((cluster, i) => ({
cluster,
clusterSize: clusterSizes[i],
selected: selectedNextId,
selected: selectedIndex,
}));

useEffect(() => {
const handleClickCluster = (_: MouseEvent, d: ClusterGraphElement) =>
const handleClickCluster = (_: PointerEvent, d: ClusterGraphElement) => {
setSelectedData(
selectedDataUpdater(d.cluster, d.cluster.commitNodeList[0].clusterId)
);
};
drawClusterGraph(svgRef, clusterGraphElements, handleClickCluster);
return () => {
destroyClusterGraph(svgRef);
};
}, [clusterGraphElements, setSelectedData]);
}, [clusterGraphElements, selectedIndex, setSelectedData]);

return <svg ref={svgRef} width={SVG_WIDTH} height={graphHeight} />;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type ClusterGraphElement = {
};

export type SVGElementSelection<T extends BaseType> = Selection<
T,
T | BaseType,
ClusterGraphElement,
SVGSVGElement | null,
unknown
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,45 @@
import type { ClusterNode } from "types";
import type { ClusterNode, SelectedDataProps } from "types";

import { COMMIT_HEIGHT, NODE_GAP } from "./ClusterGraph.const";
import {
CLUSTER_HEIGHT,
DETAIL_HEIGHT,
NODE_GAP,
SVG_MARGIN,
} from "./ClusterGraph.const";
import type { ClusterGraphElement } from "./ClusterGraph.type";

export function getClusterSizes(data: ClusterNode[]) {
return data.map((node) => node.commitNodeList.length);
}

export function getGraphHeight(clusterSizes: number[]) {
return (
clusterSizes.length * COMMIT_HEIGHT +
clusterSizes.length * CLUSTER_HEIGHT +
clusterSizes.length * NODE_GAP +
NODE_GAP
);
}

export function getClusterPosition(
d: ClusterGraphElement,
i: number,
isPrev = false
) {
const curSelected = d.selected || Infinity;
const selected = isPrev ? Infinity : curSelected;
const margin = selected >= 0 && selected < i ? DETAIL_HEIGHT : 0;
const x = SVG_MARGIN.left;
const y = SVG_MARGIN.top + i * (CLUSTER_HEIGHT + NODE_GAP) + margin;
return `translate(${x}, ${y})`;
}

export function getSelectedIndex(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(궁금) 혹시 selectedIndex를 다시 전체 data에서 찾는 구조인가요?
만약 그렇다면 추후에 component간 주고 받는 데이터에 selectedIndex를 넣어줘도 괜찮을 것 같습니다~.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞습니다...! 전체 데이터에서 selected 된 데이터를 찾는 구조인데, 추후에 selectedIndex를 추가해서 한 번더 탐색하는 작업을 줄일 수 있을 것 같습니다 :)

data: ClusterNode[],
selectedData: SelectedDataProps
) {
const selectedId = selectedData?.commitNodeList[0].clusterId;
const selectedIndex = data.findIndex(
(item) => item.commitNodeList[0].clusterId === selectedId
);
return selectedIndex;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
border-radius: $border--radius;
}

@mixin animate($animation, $duration, $method, $times) {
animation: $animation $duration $method $times;
}

@mixin keyframes($name) {
@keyframes #{$name} {
@content;
}
}

.summary__entire {
width: 85%;
margin-left: 20px;
Expand Down Expand Up @@ -158,9 +168,19 @@
.summary_detail_container {
height: 280px;
margin-top: 20px;
overflow: auto;
overflow: scroll;

&::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera*/
}

@include keyframes(open_detail) {
0% {
height: 0;
}
100% {
height: 280px;
}
}
@include animate(open_detail, 0.3s, linear, 1);
}