Menu

CalendarMonth.svelte component

Generates an SVG calendar chart.

Param Default Required Description
calcCellSize Function (w, h) => Math.min(w / 7, h / 5)
no
A function givn the canvas width and height as arguments and expects a return number that will be used as the width and height for each cell. The default will choose a size that fits seven cells across and five rows top to bottom.

Used in these examples:

SSR Examples:

<!--
  @component
  Generates an SVG calendar chart.
 -->
<script>
  import { getContext } from 'svelte';
  import { utcFormat } from 'd3-time-format';
  import { utcDay } from 'd3-time';

  const { width, height, data, x, z, zScale, extents } = getContext('LayerCake');

  /** @type {Function} [calcCellSize=(w, h) => Math.min(w / 7, h / 5)] - A function givn the canvas width and height as arguments and expects a return number that will be used as the width and height for each cell. The default will choose a size that fits seven cells across and five rows top to bottom. */
  export let calcCellSize = (w, h) => Math.min(w / 7, h / 5);

  const getDate = utcFormat('%Y-%m-%d');
  const getDayOfWeek = utcFormat('%w');
  const getWeekOfYear = utcFormat('%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;

  /* --------------------------------------------
   * Calculate what month we're in and generate the full days of that month
   */
  $: {
    const minDate = $extents.x[0];
    const parts = minDate.split('-').map(d => +d);

    days = utcDay.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(Date.UTC(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"><title>{getDate(day)}</title></rect
  >
{/each}

<style>
  .day {
    stroke: #000;
    stroke-width: 1;
    fill: #fff;
  }
</style>