Published unlisted
Edited
Jun 24, 2022
Insert cell
Changed in base
-
# drawCalendar _see also https://observablehq.com/@observablehq/calendar-component_
+
# drawCalendar
Insert cell
Added in base
### Example 1: mark different date intervals on calender
Insert cell
Changed in base
-
drawCalendar({ markDates: [ { start: new Date("2022-02-08"), end: new Date("2022-04-13"), name: "vacation" }, { start: new Date("2022-04-22"), end: new Date("2022-04-26"), name: "vacation2" } ] })
+
drawCalendar(new Date("2022-03-08"), new Date("2022-04-13"))
Insert cell
Added in base
### Example 2: Set calendar range with overallDates 
Insert cell
Added in base
drawCalendar({
  markDates: [
    {
      start: new Date("2022-04-08"),
      end: new Date("2022-04-13"),
      name: "vacation"
    },
    {
      start: new Date("2022-04-22"),
      end: new Date("2022-04-26"),
      name: "vacation2"
    }
  ],
  overallDates: {
    start: new Date("2022-02-01"),
    end: new Date("2022-04-30")
  }
})
Insert cell
Added in base
### Example 3: Passing a dataset with dates and a value column 
Insert cell
Added in base
drawCalendar({
  dates: {
    data: aapl.map((d) => ({ ...d, date: d.Date })).filter((d, i) => i < 48),
    value: "Close"
  }
})
Insert cell
Added in base
### drawCalendar Functions 
Insert cell
Changed in base
-
function drawCalendar(_config) { const dataCal = _config.dates ? _config.dates.data : getDaysArray(_config.markDates, _config.overallDates); if (!dataCal.months) { dataCal.months = 3; // temporary hack !! compute months for given data }
+
function drawCalendar(start, end) { // start and end dates of some period, e.g. drawCalendar(new Date("3-8-2022"), new Date("4-13-2022")) const dataCal = getDaysArray(start, end);
return Plot.plot({
-
width: 300 * dataCal.months, height: 300 * Math.ceil(dataCal.months / 3),
+
height: 300, // caption: html`This chart is a <b>based on local dates</b>. `,
marginLeft: 70, style: { fontSize: 16 }, x: { // padding: 0, tickFormat: Plot.formatWeekday("de"), tickSize: 0, domain: [1, 2, 3, 4, 5, 6, 0], axis: "top" }, y: { axis: null }, color: { //scheme: myScheme
-
// range: ["green", "steelblue", "orange"] legend: true
},
+
fx: { axis: "top", tickFormat: Plot.formatMonth("de", "long") },
fy: {
-
tickFormat: d3.format("d") // to show the year as 2022 instead of 2,022
+
// tickFormat: Plot.formatY("de"),
}, facet: { data: dataCal,
-
// x: (d) => d.date.getUTCMonth() % 3,
x: (d) => d.date.getUTCMonth(), y: (d) => d.date.getUTCFullYear(),
-
// y: (d) => Math.floor(d.date.getUTCMonth() / 3),
marginLeft: 70 }, marks: [
-
_config.dates ? null : Plot.cell(dataCal, { // lay the days out in the x direction based on the week of the year // x: d => d3.utcWeek.count(d3.utcYear(d.date), d.date),
+
Plot.cell(dataCal, { // lay the days out in the x direction based on the week of the year // x: d => d3.utcWeek.count(d3.utcYear(d.date), d.date),
-
y: (d) => d3.utcMonday.count(d3.utcMonth(d.date), d.date), // monday-based layout // lay the days out in the x direction based on day of the week x: (d) => d.date.getUTCDay(), stroke: (d) => (d.name ? "black" : "lightgrey"), fill: (d) => (d.name ? "steelblue" : "none"), // fill: "travel", title: (d) => d.date }), _config.dates ? null : Plot.text(dataCal, { y: (d) => d3.utcMonday.count(d3.utcMonth(d.date), d.date), // monday-based layout // lay the days out in the x direction based on day of the week x: (d) => d.date.getUTCDay(), text: (d) => d.date.getUTCDate(), fill: (d) => (d.name ? "white" : "lightgrey"), fontSize: 20 }),
+
y: (d) => d3.utcMonday.count(d3.utcMonth(d.date), d.date), // monday-based layout // lay the days out in the x direction based on day of the week x: (d) => d.date.getUTCDay(), stroke: (d) => (d.travel ? "black" : "lightgrey"), fill: (d) => (d.travel ? "steelblue" : "none"), title: (d) => d.date }), Plot.text(dataCal, { y: (d) => d3.utcMonday.count(d3.utcMonth(d.date), d.date), // monday-based layout // lay the days out in the x direction based on day of the week x: (d) => d.date.getUTCDay(), text: (d) => d.date.getUTCDate(), fill: (d) => (d.travel ? "white" : "lightgrey"), fontSize: 20 })
/* Plot.text(dataCal, { y: (d) => d3.utcMonday.count(d3.utcMonth(d.date), d.date), // monday-based layout // lay the days out in the x direction based on day of the week x: (d) => d.date.getUTCDay(), text: (d) => d.date.getUTCDate(), fill: (d) => "red", dx: 10, dy: 10, fontSize: 10
-
}),
+
})
*/
-
// data set and value is provided _config.dates ? Plot.cell(dataCal, { // lay the days out in the x direction based on the week of the year // x: d => d3.utcWeek.count(d3.utcYear(d.date), d.date), y: (d) => d3.utcMonday.count(d3.utcMonth(d.date), d.date), // monday-based layout // lay the days out in the x direction based on day of the week x: (d) => d.date.getUTCDay(), // stroke: (d) => (d.name ? "black" : "lightgrey"), fill: _config.dates.value, title: (d) => d.date }) : null, Plot.axisFx({ axis: "top", dy: -20, tickFormat: Plot.formatMonth("de", "long") }) ], ..._config.plotOptions // override with plotOptions if there are any
+
]
}); }
Insert cell
Changed in base
-
function getDaysArray(markDates, overallDates = {}) { const firstDay = overallDates.start ? overallDates.start : new Date( Date.UTC( markDates[0].start.getUTCFullYear(), markDates[0].start.getUTCMonth(), 1 ) ); const lastDay = overallDates.end ? overallDates.end : previousDay( new Date( Date.UTC( markDates[markDates.length - 1].end.getUTCFullYear(), markDates[markDates.length - 1].end.getUTCMonth() + 1, 1 ) ) ); const calendarDays = d3.utcDay.range(firstDay, nextDay(lastDay));
+
function getDaysArray(start, end) { let dstart = new Date( Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate()) ); let arr = []; // days before travel from beginning of month const firstDayCurrentMonth = new Date( Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), 1) ); for ( let dt = new Date(firstDayCurrentMonth); dt < new Date(start); dt.setUTCDate(dt.getUTCDate() + 1) ) { arr.push({ date: new Date(dt), travel: false }); }
-
const markDatesMap = new Map(); for (let interval of markDates) { let mDays = d3.utcDay.range(interval.start, nextDay(interval.end)); for (let mDay of mDays) { markDatesMap.set( mDay.toString(), interval.name ? interval.name : "marked" ); }
+
// travel dates for ( let dt = new Date(start); dt <= new Date(end); dt.setUTCDate(dt.getUTCDate() + 1) ) { arr.push({ date: new Date(dt), travel: true });
}
-
let res = []; res = calendarDays.map((d) => ({ date: d, name: markDatesMap.get(d.toString()) }));
+
// days after travel until end of month const lastDayOfMonth = new Date( Date.UTC(end.getFullYear(), end.getMonth() + 1, 0) ); let lastTravelDay = new Date(end); lastTravelDay.setUTCDate(end.getUTCDate() + 1);
-
res.months = d3.utcMonth.count(firstDay, lastDay) + 1; return res;
+
for ( let dt = new Date(lastTravelDay); dt < new Date(lastDayOfMonth); dt.setUTCDate(dt.getUTCDate() + 1) ) { arr.push({ date: new Date(dt), travel: false }); } return arr;
}
Insert cell
Added in base
function nextDay(currentDay) {
  let nextDay = new Date(currentDay);
  nextDay.setDate(currentDay.getDate() + 1);
  return nextDay;
}
Insert cell
Added in base
function previousDay(currentDay) {
  let previousDay = new Date(currentDay);
  previousDay.setDate(currentDay.getDate() - 1);
  return previousDay;
}
Insert cell
Added in base
### Documentation
Insert cell
Added in base
~~~js
config = {
  
  // property markDates
  // time interval to be marked - array of start and end dates in temporal order
  // recommended date format: "YYYY-MM-DD"
  // e.g.
  markDates: [ { 
    start: new Date("2022-03-08"),
    end: new Date("2022-04-13"),
    name: "vacation" // optional
  }],

  // property overallDates
  // the overall time interval to be visualized - object with start and end dates
  // default: the first of the first month to be marked - the last of the last month to be marked
  // e.g.
  overallDates: { 
    start: new Date("2022-01-01"),
    end: new Date("2022-12-31")
  },
  
  // property dates
  // a dataset with dates is passed to be visualized. The column date must refer to the dates
  // e.g.
  dates: {
    data: aapl.map((d) => ({ ...d, date: d.Date })), 
    value: "Close",
  }
  
  // property plotOptions
  // any Plot option can be specified
  // e.g.
  plotOptions: {
    height: 800,
    width: 800,
  }
}
~~~
Insert cell
Added in base
### Test zone
Insert cell
Added in base
a = getDaysArray([
  {
    start: new Date("2022-03-08"),
    end: new Date("2022-09-13"),
    name: "Hase"
  }
])
Insert cell
Added in base
test({
  markDates: [
    {
      start: new Date("2022-03-08"),
      end: new Date("2022-04-13")
    }
  ]
})
Insert cell
Added in base
function test(_config) {
  const markDates = _config.markDates;
  const overallDates = _config.overallDates ? _config.overallDates : {};

  const firstDay = overallDates.start
    ? overallDates.start
    : new Date(
        Date.UTC(
          markDates[0].start.getUTCFullYear(),
          markDates[0].start.getUTCMonth(),
          1
        )
      );
  const lastDay = overallDates.end
    ? overallDates.end
    : new Date(
        Date.UTC(
          markDates[markDates.length - 1].end.getUTCFullYear(),
          markDates[markDates.length - 1].end.getUTCMonth(),
          1
        )
      );
  return d3.utcDay();
}
Insert cell
Added in base
cal = d3.utcDay.range(new Date("2022-03-08"), nextDay(new Date("2022-04-13")))
Insert cell
Added in base
d3.utcDay.range(new Date("2022-04-01"), nextDay(new Date("2022-04-30")))
Insert cell
Added in base
cal.map((d) => d.toString()).includes(new Date("2022-03-05").toString())
Insert cell
Added in base
d3.utcDay(new Date().setDate(new Date("2021-02-13").getDate() + 0))
Insert cell
Added in base
d3.utcMonth.ceil(new Date("2021-02-13"))
Insert cell