-# drawCalendar _see also https://observablehq.com/@observablehq/calendar-component_+# drawCalendar
### Example 1: mark different date intervals on calender
-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"))
### Example 2: Set calendar range with overallDates
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") } })
### Example 3: Passing a dataset with dates and a value column
drawCalendar({ dates: { data: aapl.map((d) => ({ ...d, date: d.Date })).filter((d, i) => i < 48), value: "Close" } })
### drawCalendar Functions
-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+]}); }
-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;}
function nextDay(currentDay) { let nextDay = new Date(currentDay); nextDay.setDate(currentDay.getDate() + 1); return nextDay; }
function previousDay(currentDay) { let previousDay = new Date(currentDay); previousDay.setDate(currentDay.getDate() - 1); return previousDay; }
### Documentation
~~~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, } } ~~~
### Test zone
a = getDaysArray([ { start: new Date("2022-03-08"), end: new Date("2022-09-13"), name: "Hase" } ])
test({ markDates: [ { start: new Date("2022-03-08"), end: new Date("2022-04-13") } ] })
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(); }
cal = d3.utcDay.range(new Date("2022-03-08"), nextDay(new Date("2022-04-13")))
d3.utcDay.range(new Date("2022-04-01"), nextDay(new Date("2022-04-30")))
cal.map((d) => d.toString()).includes(new Date("2022-03-05").toString())
d3.utcDay(new Date().setDate(new Date("2021-02-13").getDate() + 0))
d3.utcMonth.ceil(new Date("2021-02-13"))