<script>
import { LayerCake, ScaledSvg } from 'layercake';
import { nest } from 'd3-collection';
import { scaleQuantize } from 'd3-scale';
import CalendarMonth from './_components/CalendarMonth.svelte';
import dates from './_data/dates.csv';
const monthNames = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
const datesTransformed = dates.map(row => {
row.date = new Date(row.timestring);
return row;
});
const gutter = 10;
const seriesColors = ['#fff5cc', '#ffeba9', '#ffe182', '#ffd754', '#ffcc00'];
const byMonthByDate = nest()
.key(d => d.date.getUTCMonth())
.key(d => d.timestring.split('T')[0])
.entries(datesTransformed);
const sortedData = byMonthByDate.sort((a, b) => a.key - b.key);
</script>
{#each sortedData as month, i}
<div
class="chart-container"
style="width:calc({100 / sortedData.length}% - {gutter}px);{i === 0
? `margin-right:${gutter * 2}px`
: ''}"
data-month={monthNames[+month.key + 1]}
>
<LayerCake
ssr
percentRange
padding={{ top: 1, right: 1, bottom: 1, left: 1 }}
x="key"
z={d => d.values.length}
zScale={scaleQuantize()}
zRange={seriesColors}
data={month.values}
>
<ScaledSvg>
<CalendarMonth calcCellSize={() => 100 / 7} />
</ScaledSvg>
</LayerCake>
</div>
{/each}
<style>
.chart-container {
--margin-top: 25px;
display: inline-block;
position: relative;
vertical-align: top;
height: calc(250px - var(--margin-top));
margin-top: var(--margin-top);
}
.chart-container:before {
content: attr(data-month);
position: absolute;
top: 0;
left: 0;
transform: translate(0, -100%);
}
</style>
<script>
import { getContext } from 'svelte';
import { timeFormat } from 'd3-time-format';
import { timeDay } from 'd3-time';
const { width, height, data, x, z, zScale, extents } = getContext('LayerCake');
export let calcCellSize = (w, h) => Math.min(w / 7, h / 5);
const getDayOfWeek = timeFormat('%w');
const getWeekOfYear = timeFormat('%U');
$: count = date => {
const stringDate = date.toISOString().split('T')[0];
const days = $data.filter(d => $x(d) === stringDate)[0];
if (days) {
return $z(days);
}
return 0;
};
$: fillColor = day => {
const n = count(day);
return n ? $zScale(n) : '#fff';
};
$: cellSize = calcCellSize($width, $height);
let days;
$: {
const minDate = $extents.x[0];
const parts = minDate.split('-').map(d => +d);
days = timeDay.range(
new Date(Date.UTC(parts[0], parts[1] - 1, 1)),
new Date(Date.UTC(parts[0], parts[1], 1))
);
}
$: rectX = day => getDayOfWeek(day) * cellSize;
$: rectY = day => {
const startWeek = getWeekOfYear(new Date(day.getUTCFullYear(), day.getUTCMonth(), 1));
const thisWeek = getWeekOfYear(day);
const weekDiff = thisWeek - startWeek;
return weekDiff * cellSize;
};
function showCount(day) {
console.log(day, count(day));
}
</script>
{#each days as day}
<rect
class="day"
width={cellSize}
height={cellSize}
x={rectX(day)}
y={rectY(day)}
style="fill:{fillColor(day)};"
on:mouseenter={showCount(day)}
role="tooltip"
/>
{/each}
<style>
.day {
stroke: #000;
stroke-width: 1;
fill: #fff;
}
</style>
timestring
2018-07-22T22:25:55Z
2018-07-22T19:35:29Z
2018-07-22T18:54:42Z
2018-07-22T02:05:59Z
2018-07-21T00:55:02Z
2018-07-21T00:53:00Z
2018-07-20T23:32:37Z
2018-07-20T17:52:55Z
2018-07-20T17:52:01Z
2018-07-20T17:32:21Z
2018-07-20T16:38:22Z
2018-07-20T16:38:20Z
2018-07-19T21:07:28Z
2018-07-19T01:36:47Z
2018-07-19T01:00:04Z
2018-07-18T20:15:35Z
2018-07-18T03:05:05Z
2018-07-18T02:56:18Z
2018-07-18T02:11:53Z
2018-07-18T02:08:49Z
2018-07-18T02:02:14Z
2018-07-17T04:13:29Z
2018-07-16T03:24:58Z
2018-07-16T03:23:55Z
2018-07-16T03:22:02Z
2018-07-15T18:37:05Z
2018-07-15T01:34:48Z
2018-07-15T01:11:38Z
2018-07-15T01:02:25Z
2018-07-14T23:32:07Z
2018-07-14T18:26:04Z
2018-07-14T18:25:35Z
2018-07-14T02:56:28Z
2018-07-13T16:33:57Z
2018-07-13T15:52:16Z
2018-07-12T20:31:12Z
2018-07-12T20:27:11Z
2018-07-12T14:17:18Z
2018-07-12T03:49:28Z
2018-07-12T03:42:33Z
2018-07-12T03:20:45Z
2018-07-12T01:47:25Z
2018-07-12T01:26:16Z
2018-07-12T01:16:55Z
2018-07-12T00:50:15Z
2018-07-11T19:16:21Z
2018-07-11T02:38:15Z
2018-07-11T01:03:55Z
2018-07-11T01:00:55Z
2018-07-11T00:59:31Z
2018-07-10T19:06:36Z
2018-07-10T19:03:18Z
2018-07-10T01:05:50Z
2018-07-09T23:43:32Z
2018-07-09T17:39:53Z
2018-07-09T17:37:15Z
2018-07-09T15:50:12Z
2018-07-09T03:47:16Z
2018-07-09T03:06:21Z
2018-07-09T02:59:34Z
2018-07-08T20:53:53Z
2018-07-07T01:37:58Z
2018-07-07T01:32:23Z
2018-07-07T01:30:09Z
2018-07-06T17:03:39Z
2018-07-06T16:00:39Z
2018-07-06T15:59:44Z
2018-07-04T22:29:55Z
2018-07-03T02:59:41Z
2018-07-03T02:58:36Z
2018-07-03T02:56:41Z
2018-07-01T02:21:56Z
2018-07-01T02:20:27Z
2018-07-01T02:15:25Z
2018-04-29T02:22:38Z
2018-04-29T02:19:25Z
2018-04-28T22:48:50Z
2018-04-28T06:52:20Z
2018-04-28T06:45:09Z
2018-04-26T21:18:11Z
2018-04-26T17:35:37Z
2018-04-26T01:52:56Z
2018-04-26T01:07:36Z
2018-04-26T01:05:37Z
2018-04-26T01:05:27Z
2018-04-26T01:00:30Z
2018-04-25T22:06:56Z
2018-04-25T21:32:42Z
2018-04-24T22:48:43Z
2018-04-24T22:48:26Z
2018-04-24T21:59:53Z
2018-04-24T21:58:36Z
2018-04-24T05:21:33Z
2018-04-21T03:36:45Z
2018-04-21T03:25:06Z
2018-04-20T17:57:25Z
2018-04-20T17:29:16Z
2018-04-20T17:24:44Z
2018-04-20T15:53:26Z
2018-04-20T15:32:59Z
2018-04-18T17:33:38Z
2018-04-18T15:28:30Z
2018-04-18T15:16:40Z
2018-04-18T15:07:48Z
2018-04-18T13:56:38Z
2018-04-18T04:46:00Z
2018-04-18T04:45:33Z
2018-04-18T02:19:48Z
2018-04-18T01:28:23Z
2018-04-17T23:11:52Z
2018-04-17T23:11:52Z
2018-04-17T23:05:10Z
2018-04-17T21:15:49Z
2018-04-17T21:15:41Z
2018-04-16T04:20:34Z
2018-04-16T04:20:15Z
2018-04-16T04:19:46Z
2018-04-16T04:18:59Z
2018-04-16T04:14:41Z
2018-04-16T03:54:01Z
2018-04-16T03:53:10Z
2018-04-15T02:28:02Z
2018-04-14T17:41:43Z
2018-04-14T04:42:10Z
2018-04-14T04:40:23Z
2018-04-14T04:40:10Z
2018-04-14T01:31:38Z
2018-04-14T00:23:02Z
2018-04-13T18:17:10Z
2018-04-13T18:08:21Z
2018-04-13T15:29:46Z
2018-04-13T15:15:33Z
2018-04-13T04:54:39Z
2018-04-13T04:27:48Z
2018-04-13T04:24:37Z
2018-04-13T04:08:45Z
2018-04-13T03:53:50Z
2018-04-13T03:42:23Z
2018-04-12T19:03:27Z
2018-04-11T00:32:39Z
2018-04-11T00:32:32Z
2018-04-09T02:57:20Z
2018-04-09T02:54:40Z
2018-04-08T04:28:23Z
2018-04-08T02:19:41Z
2018-04-08T00:50:51Z
2018-04-08T00:40:52Z
2018-04-07T21:54:06Z
2018-04-07T21:53:09Z
2018-04-07T19:09:30Z
2018-04-07T18:55:04Z
2018-04-07T18:51:44Z
2018-04-07T17:24:28Z
2018-04-07T02:31:18Z
2018-04-05T04:20:20Z
2018-04-05T04:19:13Z
2018-04-05T04:16:31Z
2018-04-05T04:09:51Z
2018-04-05T04:05:10Z
2018-04-05T04:04:29Z
2018-04-05T04:02:12Z
2018-04-05T03:59:43Z
2018-04-05T03:47:58Z
2018-04-05T03:43:55Z
2018-04-05T03:29:56Z
2018-04-05T03:16:56Z
2018-04-05T02:21:36Z
2018-04-05T01:31:23Z
2018-04-05T01:30:41Z
2018-04-04T02:47:42Z
2018-04-04T02:45:33Z
2018-04-04T02:45:05Z
2018-04-03T23:56:11Z
2018-04-03T23:55:17Z
2018-04-03T23:54:47Z
2018-04-03T23:54:37Z
2018-04-03T23:32:11Z
2018-04-03T05:21:21Z
2018-04-03T05:21:09Z
2018-04-03T05:19:31Z
2018-04-03T05:08:45Z
2018-04-03T05:00:12Z
2018-04-03T04:56:14Z
2018-04-03T04:55:39Z
2018-04-03T02:51:39Z
2018-04-03T02:02:43Z
2018-04-03T02:02:36Z
2018-04-03T02:01:59Z
2018-04-03T02:01:27Z
2018-04-03T00:32:13Z
2018-04-03T00:31:16Z
2018-04-03T00:29:42Z
2018-04-03T00:27:39Z
2018-04-02T03:33:34Z
2018-04-02T01:14:13Z
2018-04-02T01:09:56Z
2018-04-02T00:59:54Z
2018-04-01T05:22:24Z
2018-04-01T04:30:49Z
2018-04-01T04:30:25Z