Public
Edited
Jul 25, 2024
4 stars
Insert cell
Insert cell
Insert cell
viewof selectedInterval = timeController()
Insert cell
selectedInterval
Insert cell
{
let t = new TimeInterval(selectedInterval);
return t.getTimeres();
}
Insert cell
{
let t = new TimeInterval(selectedInterval);
return t.tsTo().toString();
}
Insert cell
{
let t = new TimeInterval(selectedInterval);
return t.getValues();
}
Insert cell
class TimeInterval {
constructor({ zoom = 1, period = 0, maxZoom = 4 } = {}) {
this.zoom = zoom;
this.period = period;
this.maxZoom = maxZoom;
}
reset() {
this.period = 0;
}
zoomOut() {
this.period = Math.floor(this.period / this.getZoomItems(this.zoom));
this.zoom += 1;
if (this.zoom > this.maxZoom) this.zoom = this.maxZoom;
}
zoomIn() {
this.zoom -= 1;
if (this.zoom < 0) this.zoom = 0;
this.period = this.period * this.getZoomItems(this.zoom);
}
back() {
this.period += 1;
}
getZoomItems(level) {
switch (level) {
case 3:
return 12;
case 2:
return 4;
case 1:
return 7;
case 0:
return 24;
default:
return 0;
}
}
forward() {
this.period -= 1;
if (this.period < 0) {
this.period = 0;
}
}
// This function is specific for homey insights that operates with some specific tags for fetching data.
// can not be used to anything else!
getTimeres() {
// Values supported by homey: lastHour, last6Hours, last24Hours,
// last7Days, last14Days, last31Days,
// today, thisWeek, thisMonth, thisYear,
// yesterday, lastWeek, lastMonth, lastYear, last2Years

if (this.zoom === 0) {
// Hour
if (this.period === 0) return "lastHour";
if (this.period <= 6) return "last6Hours";
if (this.period <= 24) return "last24Hours";
if (this.period <= 48) return "yesterday";
if (this.period <= 24 * 7) return "last14Days";
if (this.period <= 24 * 31) return "last31Days";
return null;
} else if (this.zoom === 1) {
if (this.period === 0) return "today";
if (this.period === 1) return "yesterday";
if (this.period <= 7) return "last7Days";
if (this.period <= 14) return "last14Days";
if (this.period <= 31) return "last31Days";
if (this.period <= 2 * 31) return "lastMonth";
return null;
} else if (this.zoom === 2) {
// Week
if (this.period === 0) return "thisWeek";
if (this.period === 1) return "lastWeek";
if (this.period <= 4) return "last31Days";
if (this.period <= 9) return "lastMonth";
if (this.period <= 52) return "thisYear";
return null;
} else if (this.zoom === 3) {
// Month
if (this.period === 0) return "thisMonth";
if (this.period === 1) return "lastMonth";
if (this.period <= 12) return "thisYear";
if (this.period <= 24) return "lastYear";
return null;
} else if (this.zoom === 4) {
// Year
if (this.period === 0) return "thisYear";
if (this.period <= 1) return "lastYear";
return null;
}
return null;
}
getValues() {
return {
zoom: this.zoom,
period: this.period
};
}
tsFrom() {
let currentDate = new Date();
currentDate.setMinutes(0, 0, 0);
if (this.zoom === 0) {
currentDate.setHours(currentDate.getHours() - this.period);
} else if (this.zoom === 1) {
currentDate.setDate(currentDate.getDate() - this.period);
currentDate.setHours(0, 0, 0, 0);
} else if (this.zoom === 2) {
let dayOfWeek = currentDate.getDay();
// Calculate the difference in days to the nearest Monday
// If it's Sunday (0), we need to go back 6 days to the previous Monday
let diffToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
console.log("Diff to monday", diffToMonday);
currentDate.setDate(currentDate.getDate() + diffToMonday);
currentDate.setDate(currentDate.getDate() - this.period * 7);
currentDate.setHours(0, 0, 0, 0);
} else if (this.zoom === 3) {
currentDate.setMonth(currentDate.getMonth() - this.period);
currentDate.setHours(0, 0, 0, 0);
currentDate.setDate(1);
} else if (this.zoom === 4) {
currentDate.setFullYear(currentDate.getFullYear() - this.period);
currentDate.setHours(0, 0, 0, 0);
currentDate.setMonth(0);
currentDate.setDate(1);
}
return currentDate;
}
tsTo() {
let c = this.clone();
c.period -= 1;
return c.tsFrom();
}
toString() {
if (this.zoom === 0) {
let formatter = new Intl.DateTimeFormat("nb-NO", {
weekday: "long",
day: "numeric",
hour: "2-digit",
minute: "2-digit"
});
if (this.period === 0) {
return "denne timen";
} else if (this.period === 1) {
return "forrige time";
} else {
return formatter.format(this.tsFrom());
}
} else if (this.zoom === 1) {
let formatter = new Intl.DateTimeFormat("nb-NO", {
weekday: "long",
day: "numeric",
month: "short"
});
if (this.period === 0) {
return "i dag";
} else if (this.period === 1) {
return "i går";
} else {
return formatter.format(this.tsFrom());
}
} else if (this.zoom === 2) {
let formatter = new Intl.DateTimeFormat("nb-NO", {
weekday: "long",
day: "numeric",
month: "short"
});
if (this.period === 0) {
return "denne uka";
} else if (this.period === 1) {
return "forrige uke";
} else {
return "Uke som starter " + formatter.format(this.tsFrom());
}
} else if (this.zoom === 3) {
let formatter = new Intl.DateTimeFormat("nb-NO", {
month: "long",
year: "numeric"
});
if (this.period === 0) {
return "denne måneden";
} else if (this.period === 1) {
return "forrige måned";
} else {
return formatter.format(this.tsFrom());
}
} else if (this.zoom === 4) {
let formatter = new Intl.DateTimeFormat("nb-NO", {
year: "numeric"
});
if (this.period === 0) {
return "i år";
} else if (this.period === 1) {
return "i fjor";
} else {
return formatter.format(this.tsFrom());
}
}
return "NA";
}
disableBack() {
return this.period > 10;
}
disableForward() {
return this.period === 0;
}
disableZoomIn() {
return this.zoom <= 0;
}
disableZoomOut() {
return this.zoom >= this.maxZoom;
}
disableNow() {
return this.period <= 0;
}

clone() {
return new TimeInterval({ zoom: this.zoom, period: this.period });
}
}
Insert cell
timeController = ({ period = 0, zoom = 1 } = {}) => {
const zoomOutput = htl.html`<output>`;
const periodOutput = htl.html`<output>`;
const timeText = htl.html`<output>`;

let buttonBack = htl.html`<button type="button">⬅️ forrige</button>`;
let buttonNow = htl.html`<button type="button">🎯 nå</button>`;
let buttonForward = htl.html`<button type="button" >neste ➡️</button>`;
let buttonZoomIn = htl.html`<button type="button">🔍 +</button>`;
let buttonZoomOut = htl.html`<button type="button">🔍 -</button>`;

const node = htl.html`<div style="padding: 10px 2px; font-family: sans-serif; font-size: 14px;">
${buttonBack}
${buttonNow}
${buttonForward}
${buttonZoomIn}
${buttonZoomOut}
<div style="display: inline; padding-left: 1em">${timeText}</div>
</div>`;

buttonBack.onclick = () => {
let ti = getTI();
ti.back();
setValues(node, ti);
};

buttonForward.onclick = () => {
let ti = getTI();
ti.forward();
setValues(node, ti);
};
buttonNow.onclick = () => {
let ti = getTI();
ti.reset();
setValues(node, ti);
};
buttonZoomIn.onclick = () => {
let ti = getTI();
ti.zoomIn();
setValues(node, ti);
};
buttonZoomOut.onclick = () => {
let ti = getTI();
ti.zoomOut();
setValues(node, ti);
};

function setValues(input, ti) {
let tivalues = ti.getValues();
period = tivalues.period;
zoom = tivalues.zoom;
input.value = ti.getValues();
input.dispatchEvent(new Event("input", { bubbles: true }));
}
function getTI(input) {
let ti = new TimeInterval({ zoom, period });
return ti;
}

// Update the display whenever the value changes
Object.defineProperty(node, "value", {
get() {
return { zoom: zoom, period: period };
},
set(v) {
console.log("SET()", v);
let x = new TimeInterval({ zoom: v.zoom, period: v.period });
zoomOutput.value = v.zoom;
periodOutput.value = v.period;
timeText.value = x.toString();

console.log("Setting periodoutput", v.period);

if (x.disableBack()) {
buttonBack.disabled = true;
} else {
buttonBack.removeAttribute("disabled");
}

if (x.disableNow()) {
buttonNow.disabled = true;
} else {
buttonNow.removeAttribute("disabled");
}
if (x.disableForward()) {
buttonForward.disabled = true;
} else {
buttonForward.removeAttribute("disabled");
}

if (x.disableZoomIn()) {
buttonZoomIn.disabled = true;
} else {
buttonZoomIn.removeAttribute("disabled");
}

if (x.disableZoomOut()) {
buttonZoomOut.disabled = true;
} else {
buttonZoomOut.removeAttribute("disabled");
}
}
});
node.value = {
zoom,
period
};

return node;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more