Skip to content

Commit 7809ba0

Browse files
committed
Merge branch 'git-graph' into hackape/codestyle-fix
# Conflicts: # app/components/Git/GitGraph/actions.js # app/components/Git/GitGraph/helpers/hex2rgb.js # app/components/Git/GitGraph/state.js
2 parents 0f8a0a4 + 677caf7 commit 7809ba0

File tree

12 files changed

+919
-558
lines changed

12 files changed

+919
-558
lines changed

app/components/Git/GitGraph/GitGraph.jsx

Lines changed: 144 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,161 @@
11
import React, { Component } from 'react'
22
import PropTypes from 'prop-types'
3+
import roundVertices from './helpers/roundVertices'
34

4-
const pathData = () => {
5-
return {
5+
const pdFactory = halfRowHeight => () => {
6+
const obj = {
67
data: [],
7-
moveTo (x, y) {
8-
this.data.push(`M${x},${y}`)
9-
return this
10-
},
11-
lineTo (x, y) {
12-
this.data.push(`L${x},${y}`)
8+
push (x, y, isMerged) {
9+
if (this.data.length === 0) {
10+
this.data.push([x, y])
11+
return this
12+
}
13+
14+
const lastPos = this.data[this.data.length - 1]
15+
if (lastPos[0] !== x) {
16+
if (isMerged) { // prefer go straight first, then switch lane
17+
this.data.push([lastPos[0], lastPos[1] - halfRowHeight])
18+
this.data.push([x, y])
19+
} else { // prefer switch lane first, then go straight
20+
this.data.push([x, lastPos[1] - halfRowHeight])
21+
this.data.push([x, y])
22+
}
23+
} else {
24+
this.data.push([x, y])
25+
}
26+
27+
const length = this.data.length
28+
29+
if (length >= 3 && this.data[length - 1][0] === this.data[length - 2][0] === this.data[length - 3][0]) {
30+
const last = this.data.pop()
31+
this.data.pop() // pluck the second to last pos, it's useless
32+
this.data.push(last)
33+
}
1334
return this
1435
},
36+
1537
value () {
16-
return this.data.join('')
38+
return roundVertices(this.data).reduce((acc, point, index) => {
39+
if (index === 0) {
40+
acc += `M${point[0]},${point[1]}`
41+
} else if (point.length === 2) {
42+
acc += `L${point[0]},${point[1]}`
43+
} else {
44+
const [x_s, y_s] = point[0] // startPoint
45+
const [x_c, y_c] = point[1] // ctrlPoint
46+
const [x_e, y_e] = point[2] // endPoint
47+
48+
acc += `L${x_s},${y_s}`
49+
acc += `C${x_c},${y_c},${x_e},${y_e},${x_e},${y_e}`
50+
}
51+
52+
return acc
53+
}, '')
1754
}
1855
}
56+
57+
return obj
1958
}
2059

2160
class GitGraph extends Component {
22-
constructor (props) {
23-
super(props)
24-
this.commitsCount = 0
25-
if (props.commits && props.commits.length) {
26-
this.commitsCount = props.commits.length
27-
}
61+
shouldComponentUpdate (nextProps) {
62+
return (
63+
!this.props.commitsState ||
64+
this.props.commitsState !== nextProps.commitsState ||
65+
this.props.rowHeight !== nextProps.rowHeight ||
66+
this.props.colWidth !== nextProps.colWidth
67+
)
2868
}
2969

30-
shouldComponentUpdate () {
31-
if (this.commitsCount === this.props.commits.length) {
32-
return false
33-
} else {
34-
this.commitsCount = this.props.commits.length
35-
return true
36-
}
37-
}
70+
posX = (col) => (col + 1) * this.props.colWidth
71+
posY = (row) => (row + 0.5) * this.props.rowHeight
3872

3973
render () {
40-
const { commits, circleRadius, colWidth, rowHeight } = this.props
41-
const posX = col => (col + 1) * colWidth
42-
const posY = row => (row + 1) * rowHeight - rowHeight / 2
43-
const pathProps = { strokeWidth: 2, fill: 'none' }
74+
const orphanage = new Map() // a place to shelter children who haven't found their parents yet
75+
76+
const state = this.props.commitsState
77+
const getCol = state.getCol
78+
79+
const commits = Array.from(state.commits.values())
80+
const { circleRadius, colWidth, rowHeight } = this.props
81+
const { posX, posY } = this
82+
83+
const pd = pdFactory(rowHeight / 2)
4484

4585
let pathsList = []
4686
let circlesList = []
4787
let maxCol = 0
88+
4889
commits.forEach((commit, commitIndex) => {
90+
if (!commit.isRoot) {
91+
// register parent count of this commit
92+
orphanage.set(commit.id, commit.parentIds.length)
93+
}
4994
maxCol = Math.max(maxCol, commit.col)
5095

5196
const x = posX(commit.col)
5297
const y = posY(commitIndex)
5398

5499
// draw path from current commit to its children
55100
const paths = commit.children.map((child) => {
56-
const childIndex = commits.indexOf(child)
57-
const pathKey = `p_${commit.id}_${child.id}`
58-
59-
let d, strokeColor
60-
// case 1: child on the same col, draw a straight line
61-
if (child.col === commit.col) {
62-
d = pathData()
63-
.moveTo(x, y)
64-
.lineTo(posX(child.col), posY(childIndex))
65-
.value()
66-
strokeColor = child.branch.color
101+
if (orphanage.has(child.id)) {
102+
const parentCount = orphanage.get(child.id) - 1
103+
if (parentCount <= 0) {
104+
orphanage.delete(child.id)
105+
} else {
106+
orphanage.set(child.id, parentCount)
107+
}
67108
}
68-
// case 2: child has one parent, that's a branch out
69-
else if (child.parentIds.length === 1) {
70-
d = pathData()
71-
.moveTo(x, y)
72-
.lineTo(posX(child.col), y - rowHeight/2)
73-
.lineTo(posX(child.col), posY(childIndex))
74-
.value()
75-
strokeColor = child.branch.color
109+
110+
const childIndex = commits.indexOf(child)
111+
const pathKey = `p_${commit.shortId}_${child.shortId}`
112+
113+
let d, strokeColor, pathLaneId, isMerged = false
114+
switch (commit.relationToChild(child)) {
115+
case 'NORMAL':
116+
strokeColor = child.lane.color
117+
pathLaneId = child.laneId
118+
break
119+
case 'DIVERGED':
120+
strokeColor = child.lane.color
121+
pathLaneId = child.laneId
122+
break
123+
case 'MERGED':
124+
strokeColor = commit.lane.color
125+
pathLaneId = commit.laneId
126+
isMerged = true
127+
break
76128
}
77-
// case 3: child has more than one parent
78-
else {
79-
// case 3-1: if current commit is base of merge, that's a branch out, too
80-
if (commit.isBaseOfMerge(child)) {
81-
d = pathData()
82-
.moveTo(x, y)
83-
.lineTo(posX(child.col), y - rowHeight/2)
84-
.lineTo(posX(child.col), posY(childIndex))
85-
.value()
86-
strokeColor = child.branch.color
87-
}
88-
// case 3-2: other than that, it's a merge
89-
else {
90-
d = pathData()
91-
.moveTo(x, y)
92-
.lineTo(x, posY(childIndex) + rowHeight/2)
93-
.lineTo(posX(child.col), posY(childIndex))
94-
.value()
95-
strokeColor = commit.branch.color
129+
130+
d = pd().push(x, y)
131+
132+
for (let i = commitIndex - 1; i >= childIndex; i--) {
133+
let colAtIndex = getCol(i, pathLaneId)
134+
if (i > childIndex) {
135+
d.push(posX(colAtIndex), posY(i))
136+
} else {
137+
d.push(posX(child.col), posY(childIndex), isMerged)
96138
}
97139
}
98140

99-
return <path d={d} id={pathKey} key={pathKey} stroke={strokeColor} {...pathProps} />
141+
d = d.value()
142+
143+
return (
144+
<path d={d}
145+
stroke={strokeColor}
146+
id={pathKey}
147+
key={pathKey}
148+
strokeWidth='2'
149+
fill='none'
150+
/>
151+
)
100152
})
101153

102154
const circle = (
103155
<circle
104156
key={`c_${commit.id}`}
105157
cx={x} cy={y} r={circleRadius}
106-
fill={commit.branch.color}
158+
fill={commit.lane.color}
107159
strokeWidth='1'
108160
stroke='#fff'
109161
/>)
@@ -112,6 +164,27 @@ class GitGraph extends Component {
112164
circlesList = circlesList.concat(circle)
113165
})
114166

167+
// render orphan pathes
168+
const orphans = Array.from(orphanage.keys()).map(id => state.commits.get(id))
169+
const lastIndex = commits.length
170+
const orphanPaths = orphans.map((orphan) => {
171+
const strokeColor = orphan.lane.color
172+
const pathLaneId = orphan.laneId
173+
const orphanIndex = orphan.index
174+
175+
let d = pd()
176+
d.push(posX(getCol(lastIndex - 1, pathLaneId)), posY(lastIndex))
177+
for (let i = lastIndex - 1; i >= orphanIndex; i--) {
178+
d.push(posX(getCol(i, pathLaneId)), posY(i))
179+
}
180+
181+
d = d.value()
182+
const pathKey = `future_${orphan.id}`
183+
return <path id={pathKey} key={pathKey} d={d} stroke={strokeColor} strokeWidth='2' fill='none' />
184+
})
185+
186+
// end render orphan pathes
187+
pathsList = pathsList.concat(orphanPaths)
115188
const width = colWidth * (maxCol + 2)
116189
if (typeof this.props.onWidthChange === 'function') this.props.onWidthChange(width)
117190

@@ -124,13 +197,12 @@ class GitGraph extends Component {
124197
}
125198

126199

127-
const { string, number, arrayOf, shape, } = PropTypes
128-
const branchType = shape({ color: string.isRequired })
200+
const { string, number, arrayOf, shape, object } = PropTypes
201+
const laneType = shape({ color: string.isRequired })
129202

130203
const commitShapeConfig = {
131204
id: string.isRequired,
132-
col: number.isRequired,
133-
branch: branchType,
205+
lane: laneType,
134206
}
135207

136208
const commitType = shape({
@@ -139,7 +211,7 @@ const commitType = shape({
139211
})
140212

141213
GitGraph.propTypes = {
142-
commits: arrayOf(commitType).isRequired,
214+
commitsState: object.isRequired,
143215
circleRadius: number.isRequired,
144216
colWidth: number.isRequired,
145217
rowHeight: number.isRequired,

0 commit comments

Comments
 (0)