Skip to content

Commit d97cbd5

Browse files
committed
add a vertical mode
1 parent 197de80 commit d97cbd5

File tree

4 files changed

+161
-42
lines changed

4 files changed

+161
-42
lines changed

README.md

Lines changed: 86 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,91 @@
11
# vue-calendar-heatmap
22

3-
> work in progress
3+
<p align="center">
4+
<img src="https://i.imgur.com/ntYYTKX.png" alt="Screenshot"/>
5+
</p>
46

5-
A calendar heatmap Vuejs component built on SVG, inspired by github's contribution calendar graph
7+
<p align="center">
8+
<a href="https://www.npmjs.com/package/vue-calendar-heatmap">
9+
<img src="https://img.shields.io/npm/v/vue-calendar-heatmap.svg"/>
10+
<img src="https://img.shields.io/npm/dm/vue-calendar-heatmap.svg"/>
11+
</a>
12+
<a href="https://vuejs.org/">
13+
<img src="https://img.shields.io/badge/vue-2.x-brightgreen.svg"/>
14+
</a>
15+
</p>
616

7-
## props
17+
A lightweight calendar heatmap Vuejs component built on SVG, inspired by github's contribution calendar graph. With vertical mode, tooltip powered by [v-tooltip](https://github.com/Akryum/v-tooltip).
818

9-
| keys | Type | Required | Exemple |
10-
|---|---|---|---|
11-
| values `values` | Array of objects with `date` and `count` keys. `date` values can be a date parseable string, a millisecond timestamp, or a Date object. `count` value should be a number. | true | `:values="[{ date: '2018-9-22', count: 6 }, ...]"` |
12-
| endDate `end-date` | Can be a date parseable string, a millisecond timestamp, or a Date object. The calendar will start automatically one year before this date. | true | `endDate="2018-9-22"` |
13-
| colorRange `color-range` | A Array of 5 strings which represents the colors of the progression. The color at `colorRange[0]` will always represent the values for a `count: 0`. The others are automatically distributed over the maximum value of count, unless you specify `max` props. | false. Default value is equal to the example | `:range-color="['ebedf0', '#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e']"`
14-
| max `max` | Any number which should be the max color | false | `:max="10"`
15-
| tooltip `tooltip` | Boolean for enabble/disable tooltip on square hover. `true` by default | false | `:tooltip="false"`
16-
| tooltipUnit `tooltip-unit` | String representing heatmap's unit of measure. His value is `"contributions"` by default. | false | `tooltip-unit="stars"`
19+
## Getting started
20+
21+
1. Load Module
22+
``` shell
23+
npm insall --save vue-calendar-heatmap
24+
```
25+
2. Import in your vuejs application
26+
``` javascript
27+
import CalendarHeatmap from 'vue-calendar-heatmap'
28+
29+
// in a parent component script
30+
export default {
31+
components: {
32+
CalendarHeatmap
33+
},
34+
35+
data () {
36+
return {
37+
today: new Date(),
38+
values: [{ date: '2017-11-09', count: 1 }, { date: '2017-11-20', count: 1 }]
39+
}
40+
// ...
41+
}
42+
```
43+
``` html
44+
<!-- in a parent component template -->
45+
<template>
46+
<div id="parent">
47+
<calendar-heatmap :end-date="today" :values="values" />
48+
</div>
49+
</template>
50+
```
51+
52+
53+
## Availables props
54+
### **values** - `values` - required
55+
Array of objects with `date` and `count` keys. `date` values can be a date parseable string, a millisecond timestamp, or a Date object. `count` value should be a number.
56+
``` html
57+
<vue-calendar-heatmap :values="[{ date: '2018-9-22', count: 6 }, ...]" ... />
58+
```
59+
### **endDate** - `end-date` - required
60+
Can be a date parseable string, a millisecond timestamp, or a Date object. The calendar will start automatically one year before this date.
61+
``` html
62+
<vue-calendar-heatmap endDate="2018-9-22" ... />
63+
```
64+
65+
### colorRange - `color-range`
66+
A Array of 5 strings which represents the colors of the progression. The color at `colorRange[0]` will always represent the values for a `count: 0`. The others are automatically distributed over the maximum value of count, unless you specify `max` props. Default value is equal to the example.
67+
``` html
68+
<vue-calendar-heatmap :range-color="['ebedf0', '#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e']" ... />
69+
```
70+
71+
### max - `max`
72+
Any number which should be the max color.
73+
``` html
74+
<vue-calendar-heatmap :max="10" ... />
75+
```
76+
### tooltip - `tooltip`
77+
Boolean for enabble/disable tooltip on square hover. `true` by default.
78+
``` html
79+
<vue-calendar-heatmap :tooltip="false" ... />
80+
```
81+
### tooltipUnit - `tooltip-unit`
82+
String representing heatmap's unit of measure. His value is `"contributions"` by default.
83+
``` html
84+
<vue-calendar-heatmap tooltip-unit="stars" ... />
85+
```
86+
87+
### vertical - `vertical`
88+
Boolean to switch to vertical mode. `false` by default.
89+
``` html
90+
<vue-calendar-heatmap :vertical="true" ... />
91+
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vue-calendar-heatmap",
3-
"version": "0.5.2",
3+
"version": "0.6.0",
44
"description": "A calendar heatmap Vuejs component built on SVG, inspired by github's contribution calendar graph",
55
"author": "Julien Rabin <julien@wildcodeschool.fr>",
66
"scripts": {

src/App.vue

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
<template lang="pug">
2-
#app
3-
calendar-heatmap(
4-
:end-date="today",
5-
:values="values"
6-
)
1+
<template>
2+
<div id="app">
3+
<calendar-heatmap :end-date="today" :values="values" :vertical="false" />
4+
<calendar-heatmap :end-date="today" :values="values" :vertical="true" />
5+
</div>
76
</template>
87

98
<script>

src/CalendarHeatmap.vue

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,43 @@
11
<template lang="pug">
22
svg.vch__wrapper(:viewBox="viewbox")
3-
g.vch__months__labels__wrapper(:transform="monthsLabelWrapperTransform")
3+
g.vch__months__labels__wrapper(:transform="monthsLabelWrapperTransform[position]")
44
text.vch__month__label(
55
v-for="(month, index) in heatmap.firstFullWeekOfMonths",
6-
:x="SQUARE_SIZE * month.index",
7-
:y="SQUARE_SIZE - SQUARE_BORDER_SIZE"
6+
:x="getMonthLabelPostion(month).x",
7+
:y="getMonthLabelPostion(month).y"
88
) {{ lo.months[month.value] }}
99

10-
g.vch__days__labels__wrapper(:transform="daysLabelWrapperTransform")
11-
text.vch__day__label(x="0", :y="20") {{ lo.days[1] }}
12-
text.vch__day__label(x="0", :y="44") {{ lo.days[3] }}
13-
text.vch__day__label(x="0", :y="69") {{ lo.days[5] }}
10+
g.vch__days__labels__wrapper(:transform="daysLabelWrapperTransform[position]")
11+
text.vch__day__label(
12+
:x="vertical ? SQUARE_SIZE * 1 : 0",
13+
:y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 20"
14+
) {{ lo.days[1] }}
15+
text.vch__day__label(
16+
:x="vertical ? SQUARE_SIZE * 3 : 0",
17+
:y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 44"
18+
) {{ lo.days[3] }}
19+
text.vch__day__label(
20+
:x="vertical ? SQUARE_SIZE * 5 : 0",
21+
:y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 69"
22+
) {{ lo.days[5] }}
1423

15-
g.vch__legend__wrapper(:transform="legendWrapperTransform")
24+
g.vch__legend__wrapper(:transform="legendWrapperTransform[position]")
1625
text(
17-
x="-25"
18-
:y="SQUARE_SIZE + 1"
26+
:x="vertical ? SQUARE_SIZE * 1.25 : -25"
27+
:y="vertical ? 8 : SQUARE_SIZE + 1"
1928
) {{ lo.less }}
2029
rect(
2130
v-for="(color, index) in rangeColor",
2231
:key="index",
2332
:style="{ fill: color }",
2433
:width="SQUARE_SIZE - SQUARE_BORDER_SIZE",
2534
:height="SQUARE_SIZE - SQUARE_BORDER_SIZE",
26-
:x="SQUARE_SIZE * index",
27-
y="5"
35+
:x="vertical ? SQUARE_SIZE * 1.75 : SQUARE_SIZE * index",
36+
:y="vertical ? SQUARE_SIZE * (index + 1) : 5"
2837
)
2938
text(
30-
:x="SQUARE_SIZE * rangeColor.length + 1",
31-
:y="SQUARE_SIZE + 1"
39+
:x="vertical ? SQUARE_SIZE * 1.25 : SQUARE_SIZE * rangeColor.length + 1",
40+
:y="vertical ? SQUARE_SIZE * (rangeColor.length + 2) - SQUARE_BORDER_SIZE : SQUARE_SIZE + 1"
3241
) {{ lo.more }}
3342
g.vch__year__wrapper(:transform="yearWrapperTransform")
3443
g.vch__month__wrapper(
@@ -86,6 +95,10 @@ export default {
8695
tooltipUnit: {
8796
type: String,
8897
default: DEFAULT_TOOLTIP_UNIT
98+
},
99+
vertical: {
100+
type: Boolean,
101+
default: false
89102
}
90103
},
91104
@@ -96,39 +109,59 @@ export default {
96109
},
97110
98111
computed: {
112+
position () {
113+
return this.vertical ? 'vertical' : 'horizontal'
114+
},
99115
tooltipTransform () {
100116
return `translate(${this.tooltipX}, ${this.tooltipY})`
101117
},
102118
heatmap () {
103119
return new Heatmap(this.endDate, this.values, this.max)
104120
},
105121
width () {
106-
return this.DAYS_LABELS_WIDTH + (this.SQUARE_SIZE * this.heatmap.weekCount) + this.SQUARE_BORDER_SIZE
122+
return {
123+
horizontal: this.LEFT_SECTION_WIDTH + (this.SQUARE_SIZE * this.heatmap.weekCount) + this.SQUARE_BORDER_SIZE,
124+
vertical: this.LEFT_SECTION_WIDTH + (this.SQUARE_SIZE * DAYS_IN_WEEK) + this.RIGHT_SECTION_WIDTH
125+
}
107126
},
108127
heigth () {
109-
return this.WEEKS_LABELS_HEIGTH + (this.SQUARE_SIZE * DAYS_IN_WEEK) + this.SQUARE_BORDER_SIZE + this.LEGEND_HEIGTH
128+
return {
129+
horizontal: this.TOP_SECTION_HEIGTH + (this.SQUARE_SIZE * DAYS_IN_WEEK) + this.SQUARE_BORDER_SIZE + this.BOTTOM_SECTION_HEIGTH,
130+
vertical: this.TOP_SECTION_HEIGTH + (this.SQUARE_SIZE * this.heatmap.weekCount) + this.SQUARE_BORDER_SIZE
131+
}
110132
},
111133
viewbox () {
112-
return `0 0 ${this.width} ${this.heigth}`
134+
return `0 0 ${this.width[this.position]} ${this.heigth[this.position]}`
113135
},
114136
daysLabelWrapperTransform () {
115-
return `translate(0, ${this.WEEKS_LABELS_HEIGTH})`
137+
return {
138+
horizontal: `translate(0, ${this.TOP_SECTION_HEIGTH})`,
139+
vertical: `translate(${this.LEFT_SECTION_WIDTH}, 0)`
140+
}
116141
},
117142
monthsLabelWrapperTransform () {
118-
return `translate(${this.DAYS_LABELS_WIDTH}, 0)`
143+
return {
144+
horizontal: `translate(${this.LEFT_SECTION_WIDTH}, 0)`,
145+
vertical: `translate(0, ${this.TOP_SECTION_HEIGTH})`
146+
}
119147
},
120148
legendWrapperTransform () {
121-
return `translate(${this.width - (this.SQUARE_SIZE * this.rangeColor.length) - 30}, ${this.heigth - this.LEGEND_HEIGTH})`
149+
return {
150+
horizontal: `translate(${this.width[this.position] - (this.SQUARE_SIZE * this.rangeColor.length) - 30}, ${this.heigth[this.position] - this.BOTTOM_SECTION_HEIGTH})`,
151+
vertical: `translate(${this.LEFT_SECTION_WIDTH + (this.SQUARE_SIZE * DAYS_IN_WEEK)}, ${this.TOP_SECTION_HEIGTH})`
152+
}
153+
122154
},
123155
yearWrapperTransform () {
124-
return `translate(${this.DAYS_LABELS_WIDTH}, ${this.WEEKS_LABELS_HEIGTH})`
156+
return `translate(${this.LEFT_SECTION_WIDTH}, ${this.TOP_SECTION_HEIGTH})`
125157
},
126158
127159
SQUARE_BORDER_SIZE: () => SQUARE_SIZE / 5,
128160
SQUARE_SIZE () { return SQUARE_SIZE + this.SQUARE_BORDER_SIZE },
129-
WEEKS_LABELS_HEIGTH () { return SQUARE_SIZE + (SQUARE_SIZE / 2) },
130-
DAYS_LABELS_WIDTH () { return Math.ceil(SQUARE_SIZE * 2.5) },
131-
LEGEND_HEIGTH () { return SQUARE_SIZE + (SQUARE_SIZE / 2) },
161+
TOP_SECTION_HEIGTH () { return SQUARE_SIZE + (SQUARE_SIZE / 2) },
162+
RIGHT_SECTION_WIDTH () { return this.SQUARE_SIZE * 3 },
163+
BOTTOM_SECTION_HEIGTH () { return SQUARE_SIZE + (SQUARE_SIZE / 2) },
164+
LEFT_SECTION_WIDTH () { return Math.ceil(SQUARE_SIZE * 2.5) },
132165
133166
lo () {
134167
if (this.locale) {
@@ -155,10 +188,22 @@ export default {
155188
return false
156189
},
157190
getWeekPosition (index) {
191+
if (this.vertical) {
192+
return `translate(0, ${(this.SQUARE_SIZE * this.heatmap.weekCount) - ((index + 1) * this.SQUARE_SIZE)})`
193+
}
158194
return `translate(${index * this.SQUARE_SIZE}, 0)`
159195
},
160196
getDayPosition (index) {
197+
if (this.vertical) {
198+
return `translate(${index * this.SQUARE_SIZE}, 0)`
199+
}
161200
return `translate(0, ${index * this.SQUARE_SIZE})`
201+
},
202+
getMonthLabelPostion (month) {
203+
let position = { x: 0, y:0 }
204+
position.x = this.vertical ? 3 : this.SQUARE_SIZE * month.index
205+
position.y = this.vertical ? (this.SQUARE_SIZE * this.heatmap.weekCount) - (this.SQUARE_SIZE * (month.index)) - (this.SQUARE_SIZE / 4) : this.SQUARE_SIZE - this.SQUARE_BORDER_SIZE
206+
return position
162207
}
163208
}
164209
}

0 commit comments

Comments
 (0)