Archived

Documentation for Layer Cake v10, before Svelte 5 runes. Current docs at layercake.graphics

Menu

Calendar (one cake per month)

2018-04-012018-04-022018-04-032018-04-042018-04-052018-04-062018-04-072018-04-082018-04-092018-04-102018-04-112018-04-122018-04-132018-04-142018-04-152018-04-162018-04-172018-04-182018-04-192018-04-202018-04-212018-04-222018-04-232018-04-242018-04-252018-04-262018-04-272018-04-282018-04-292018-04-30
2018-07-012018-07-022018-07-032018-07-042018-07-052018-07-062018-07-072018-07-082018-07-092018-07-102018-07-112018-07-122018-07-132018-07-142018-07-152018-07-162018-07-172018-07-182018-07-192018-07-202018-07-212018-07-222018-07-232018-07-242018-07-252018-07-262018-07-272018-07-282018-07-292018-07-302018-07-31

A small multiple per month. The rows are grouped by month and then by day with d3-array's group, and each month's chart draws every day of that month, colored by how many rows fell on it.

This is the server-side rendered version. ssr and percentRange on <LayerCake> put the scales in percentages, so the chart renders before the browser measures it. The squares sit in a <ScaledSvg> that stretches to fit its box.

<script>
  import { LayerCake, ScaledSvg } from 'layercake';
  import { group } from 'd3-array';
  import { scaleBand, scaleQuantize } from 'd3-scale';

  import CalendarMonth from './_components/CalendarMonth.svelte';

  // The CSV rows are parsed, and their numbers typed, by @rollup/plugin-dsv. See vite.config.js
  import dates from './_data/dates.csv';

  const monthNames = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December'
  ];

  const gutter = 10;
  const seriesColors = ['#fff5cc', '#ffeba9', '#ffe182', '#ffd754', '#ffcc00'];

  /** @param {{ timestring: string }} d */
  const monthOf = d => new Date(d.timestring).getUTCMonth();
  /** @param {{ timestring: string }} d */
  const dayOf = d => d.timestring.split('T')[0];

  // Group the rows by month, then by day. Each month becomes one chart and
  // each day one row of it, colored by how many rows fell on that day.
  const byMonth = group(dates, monthOf, dayOf);
  const months = Array.from(byMonth, ([month, byDay]) => ({
    month,
    days: Array.from(byDay, ([date, rows]) => ({ date, rows }))
  })).sort((a, b) => a.month - b.month);
</script>

{#each months as { month, days }, i}
  <div
    class="chart-container"
    style="width:calc({100 / months.length}% - {gutter}px);{i === 0
      ? `margin-right:${gutter * 2}px`
      : ''}"
    data-month={monthNames[month]}
  >
    <LayerCake
      ssr
      percentRange
      padding={{ top: 1, right: 1, bottom: 1, left: 1 }}
      x="date"
      xScale={scaleBand()}
      c={d => d.rows.length}
      cScale={scaleQuantize()}
      cRange={seriesColors}
      data={days}
    >
      <ScaledSvg>
        <CalendarMonth />
      </ScaledSvg>
    </LayerCake>
  </div>
{/each}

<style>
  /* Give the wrapper a width and height. LayerCake fills it. The width is set inline below. */
  .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>