Skip to content

Commit

Permalink
add a vertical mode
Browse files Browse the repository at this point in the history
  • Loading branch information
julienr114 committed May 30, 2018
1 parent 197de80 commit d97cbd5
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 42 deletions.
97 changes: 86 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,91 @@
# vue-calendar-heatmap

> work in progress
<p align="center">
<img src="https://i.imgur.com/ntYYTKX.png" alt="Screenshot"/>
</p>

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

## props
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).

| keys | Type | Required | Exemple |
|---|---|---|---|
| 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 }, ...]"` |
| 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"` |
| 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']"`
| max `max` | Any number which should be the max color | false | `:max="10"`
| tooltip `tooltip` | Boolean for enabble/disable tooltip on square hover. `true` by default | false | `:tooltip="false"`
| tooltipUnit `tooltip-unit` | String representing heatmap's unit of measure. His value is `"contributions"` by default. | false | `tooltip-unit="stars"`
## Getting started

1. Load Module
``` shell
npm insall --save vue-calendar-heatmap
```
2. Import in your vuejs application
``` javascript
import CalendarHeatmap from 'vue-calendar-heatmap'

// in a parent component script
export default {
components: {
CalendarHeatmap
},

data () {
return {
today: new Date(),
values: [{ date: '2017-11-09', count: 1 }, { date: '2017-11-20', count: 1 }]
}
// ...
}
```
``` html
<!-- in a parent component template -->
<template>
<div id="parent">
<calendar-heatmap :end-date="today" :values="values" />
</div>
</template>
```
## Availables props
### **values** - `values` - required
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.
``` html
<vue-calendar-heatmap :values="[{ date: '2018-9-22', count: 6 }, ...]" ... />
```
### **endDate** - `end-date` - required
Can be a date parseable string, a millisecond timestamp, or a Date object. The calendar will start automatically one year before this date.
``` html
<vue-calendar-heatmap endDate="2018-9-22" ... />
```
### 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. Default value is equal to the example.
``` html
<vue-calendar-heatmap :range-color="['ebedf0', '#ebedf0', '#c0ddf9', '#73b3f3', '#3886e1', '#17459e']" ... />
```
### max - `max`
Any number which should be the max color.
``` html
<vue-calendar-heatmap :max="10" ... />
```
### tooltip - `tooltip`
Boolean for enabble/disable tooltip on square hover. `true` by default.
``` html
<vue-calendar-heatmap :tooltip="false" ... />
```
### tooltipUnit - `tooltip-unit`
String representing heatmap's unit of measure. His value is `"contributions"` by default.
``` html
<vue-calendar-heatmap tooltip-unit="stars" ... />
```
### vertical - `vertical`
Boolean to switch to vertical mode. `false` by default.
``` html
<vue-calendar-heatmap :vertical="true" ... />
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vue-calendar-heatmap",
"version": "0.5.2",
"version": "0.6.0",
"description": "A calendar heatmap Vuejs component built on SVG, inspired by github's contribution calendar graph",
"author": "Julien Rabin <[email protected]>",
"scripts": {
Expand Down
11 changes: 5 additions & 6 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<template lang="pug">
#app
calendar-heatmap(
:end-date="today",
:values="values"
)
<template>
<div id="app">
<calendar-heatmap :end-date="today" :values="values" :vertical="false" />
<calendar-heatmap :end-date="today" :values="values" :vertical="true" />
</div>
</template>

<script>
Expand Down
93 changes: 69 additions & 24 deletions src/CalendarHeatmap.vue
Original file line number Diff line number Diff line change
@@ -1,34 +1,43 @@
<template lang="pug">
svg.vch__wrapper(:viewBox="viewbox")
g.vch__months__labels__wrapper(:transform="monthsLabelWrapperTransform")
g.vch__months__labels__wrapper(:transform="monthsLabelWrapperTransform[position]")
text.vch__month__label(
v-for="(month, index) in heatmap.firstFullWeekOfMonths",
:x="SQUARE_SIZE * month.index",
:y="SQUARE_SIZE - SQUARE_BORDER_SIZE"
:x="getMonthLabelPostion(month).x",
:y="getMonthLabelPostion(month).y"
) {{ lo.months[month.value] }}

g.vch__days__labels__wrapper(:transform="daysLabelWrapperTransform")
text.vch__day__label(x="0", :y="20") {{ lo.days[1] }}
text.vch__day__label(x="0", :y="44") {{ lo.days[3] }}
text.vch__day__label(x="0", :y="69") {{ lo.days[5] }}
g.vch__days__labels__wrapper(:transform="daysLabelWrapperTransform[position]")
text.vch__day__label(
:x="vertical ? SQUARE_SIZE * 1 : 0",
:y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 20"
) {{ lo.days[1] }}
text.vch__day__label(
:x="vertical ? SQUARE_SIZE * 3 : 0",
:y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 44"
) {{ lo.days[3] }}
text.vch__day__label(
:x="vertical ? SQUARE_SIZE * 5 : 0",
:y="vertical ? SQUARE_SIZE - SQUARE_BORDER_SIZE : 69"
) {{ lo.days[5] }}

g.vch__legend__wrapper(:transform="legendWrapperTransform")
g.vch__legend__wrapper(:transform="legendWrapperTransform[position]")
text(
x="-25"
:y="SQUARE_SIZE + 1"
:x="vertical ? SQUARE_SIZE * 1.25 : -25"
:y="vertical ? 8 : SQUARE_SIZE + 1"
) {{ lo.less }}
rect(
v-for="(color, index) in rangeColor",
:key="index",
:style="{ fill: color }",
:width="SQUARE_SIZE - SQUARE_BORDER_SIZE",
:height="SQUARE_SIZE - SQUARE_BORDER_SIZE",
:x="SQUARE_SIZE * index",
y="5"
:x="vertical ? SQUARE_SIZE * 1.75 : SQUARE_SIZE * index",
:y="vertical ? SQUARE_SIZE * (index + 1) : 5"
)
text(
:x="SQUARE_SIZE * rangeColor.length + 1",
:y="SQUARE_SIZE + 1"
:x="vertical ? SQUARE_SIZE * 1.25 : SQUARE_SIZE * rangeColor.length + 1",
:y="vertical ? SQUARE_SIZE * (rangeColor.length + 2) - SQUARE_BORDER_SIZE : SQUARE_SIZE + 1"
) {{ lo.more }}
g.vch__year__wrapper(:transform="yearWrapperTransform")
g.vch__month__wrapper(
Expand Down Expand Up @@ -86,6 +95,10 @@ export default {
tooltipUnit: {
type: String,
default: DEFAULT_TOOLTIP_UNIT
},
vertical: {
type: Boolean,
default: false
}
},
Expand All @@ -96,39 +109,59 @@ export default {
},
computed: {
position () {
return this.vertical ? 'vertical' : 'horizontal'
},
tooltipTransform () {
return `translate(${this.tooltipX}, ${this.tooltipY})`
},
heatmap () {
return new Heatmap(this.endDate, this.values, this.max)
},
width () {
return this.DAYS_LABELS_WIDTH + (this.SQUARE_SIZE * this.heatmap.weekCount) + this.SQUARE_BORDER_SIZE
return {
horizontal: this.LEFT_SECTION_WIDTH + (this.SQUARE_SIZE * this.heatmap.weekCount) + this.SQUARE_BORDER_SIZE,
vertical: this.LEFT_SECTION_WIDTH + (this.SQUARE_SIZE * DAYS_IN_WEEK) + this.RIGHT_SECTION_WIDTH
}
},
heigth () {
return this.WEEKS_LABELS_HEIGTH + (this.SQUARE_SIZE * DAYS_IN_WEEK) + this.SQUARE_BORDER_SIZE + this.LEGEND_HEIGTH
return {
horizontal: this.TOP_SECTION_HEIGTH + (this.SQUARE_SIZE * DAYS_IN_WEEK) + this.SQUARE_BORDER_SIZE + this.BOTTOM_SECTION_HEIGTH,
vertical: this.TOP_SECTION_HEIGTH + (this.SQUARE_SIZE * this.heatmap.weekCount) + this.SQUARE_BORDER_SIZE
}
},
viewbox () {
return `0 0 ${this.width} ${this.heigth}`
return `0 0 ${this.width[this.position]} ${this.heigth[this.position]}`
},
daysLabelWrapperTransform () {
return `translate(0, ${this.WEEKS_LABELS_HEIGTH})`
return {
horizontal: `translate(0, ${this.TOP_SECTION_HEIGTH})`,
vertical: `translate(${this.LEFT_SECTION_WIDTH}, 0)`
}
},
monthsLabelWrapperTransform () {
return `translate(${this.DAYS_LABELS_WIDTH}, 0)`
return {
horizontal: `translate(${this.LEFT_SECTION_WIDTH}, 0)`,
vertical: `translate(0, ${this.TOP_SECTION_HEIGTH})`
}
},
legendWrapperTransform () {
return `translate(${this.width - (this.SQUARE_SIZE * this.rangeColor.length) - 30}, ${this.heigth - this.LEGEND_HEIGTH})`
return {
horizontal: `translate(${this.width[this.position] - (this.SQUARE_SIZE * this.rangeColor.length) - 30}, ${this.heigth[this.position] - this.BOTTOM_SECTION_HEIGTH})`,
vertical: `translate(${this.LEFT_SECTION_WIDTH + (this.SQUARE_SIZE * DAYS_IN_WEEK)}, ${this.TOP_SECTION_HEIGTH})`
}
},
yearWrapperTransform () {
return `translate(${this.DAYS_LABELS_WIDTH}, ${this.WEEKS_LABELS_HEIGTH})`
return `translate(${this.LEFT_SECTION_WIDTH}, ${this.TOP_SECTION_HEIGTH})`
},
SQUARE_BORDER_SIZE: () => SQUARE_SIZE / 5,
SQUARE_SIZE () { return SQUARE_SIZE + this.SQUARE_BORDER_SIZE },
WEEKS_LABELS_HEIGTH () { return SQUARE_SIZE + (SQUARE_SIZE / 2) },
DAYS_LABELS_WIDTH () { return Math.ceil(SQUARE_SIZE * 2.5) },
LEGEND_HEIGTH () { return SQUARE_SIZE + (SQUARE_SIZE / 2) },
TOP_SECTION_HEIGTH () { return SQUARE_SIZE + (SQUARE_SIZE / 2) },
RIGHT_SECTION_WIDTH () { return this.SQUARE_SIZE * 3 },
BOTTOM_SECTION_HEIGTH () { return SQUARE_SIZE + (SQUARE_SIZE / 2) },
LEFT_SECTION_WIDTH () { return Math.ceil(SQUARE_SIZE * 2.5) },
lo () {
if (this.locale) {
Expand All @@ -155,10 +188,22 @@ export default {
return false
},
getWeekPosition (index) {
if (this.vertical) {
return `translate(0, ${(this.SQUARE_SIZE * this.heatmap.weekCount) - ((index + 1) * this.SQUARE_SIZE)})`
}
return `translate(${index * this.SQUARE_SIZE}, 0)`
},
getDayPosition (index) {
if (this.vertical) {
return `translate(${index * this.SQUARE_SIZE}, 0)`
}
return `translate(0, ${index * this.SQUARE_SIZE})`
},
getMonthLabelPostion (month) {
let position = { x: 0, y:0 }
position.x = this.vertical ? 3 : this.SQUARE_SIZE * month.index
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
return position
}
}
}
Expand Down

0 comments on commit d97cbd5

Please sign in to comment.