@ribajs/moment
Version:
Moment.js module for Riba.js
218 lines (182 loc) • 5.69 kB
text/typescript
import { Component, TemplateFunction, ScopeBase } from "@ribajs/core";
interface Scope extends ScopeBase {
time: string;
countdownYears: boolean;
countdownDays: boolean;
countdownHours: boolean;
countdownMinutes: boolean;
countdownSeconds: boolean;
countdownRunning: boolean;
remainingYears: number;
remainingDays: number;
remainingHours: number;
remainingMinutes: number;
remainingSeconds: number;
}
///TODO: Add "timer" mode, which starts at a given point in time,
// and counts the time passed since than, and a mixed
// mode, which is basically a countdown and after the countdown reached 0,
// it continues as a "timer".
export class TimerComponent extends Component {
public static tagName = "moment-timer";
protected autobind = true;
static get observedAttributes(): string[] {
return [
"time",
"countdown-years",
"countdown-days",
"countdown-hours",
"countdown-minutes",
"countdown-seconds",
];
}
protected requiredAttributes(): string[] {
return ["time"];
}
public scope: Scope = {
countdownYears: false,
countdownDays: true,
countdownHours: true,
countdownMinutes: true,
countdownSeconds: true,
countdownRunning: true,
remainingYears: 0,
remainingDays: 0,
remainingHours: 0,
remainingMinutes: 0,
remainingSeconds: 0,
time: "",
};
private updateIntervalId?: number;
constructor() {
super();
}
protected connectedCallback() {
super.connectedCallback();
this.init(TimerComponent.observedAttributes);
this.startCountdownInterval();
}
protected async init(observedAttributes: string[]) {
return super.init(observedAttributes);
}
protected async beforeBind() {
return await super.beforeBind();
}
protected async afterBind() {
await super.afterBind();
}
protected updateCountdown() {
//calculate seconds until countdown is over
let remainingSeconds = this.getRemainingSeconds();
if (!remainingSeconds || remainingSeconds < 1) {
this.cancelCountdownInterval();
this.resetCountdown();
return;
}
this.scope.countdownRunning = true;
const secsOfMinute = 60;
const secsOfHour = 60 * secsOfMinute;
const secsOfDay = 24 * secsOfHour;
const secsOfYear = 365 * secsOfDay;
// TODO leap year (Schaltjahr)
if (this.scope.countdownYears) {
if (this.isYearsLastUnit()) {
this.scope.remainingYears = Math.ceil(remainingSeconds / secsOfYear);
return;
} else {
this.scope.remainingYears = Math.floor(remainingSeconds / secsOfYear);
remainingSeconds -= this.scope.remainingYears * secsOfYear;
}
}
if (this.scope.countdownDays) {
if (this.isDaysLastUnit()) {
this.scope.remainingDays = Math.ceil(remainingSeconds / secsOfDay);
return;
} else {
this.scope.remainingDays = Math.floor(remainingSeconds / secsOfDay);
remainingSeconds -= this.scope.remainingDays * secsOfDay;
}
}
if (this.scope.countdownHours) {
if (this.isHoursLastUnit()) {
this.scope.remainingHours = Math.ceil(remainingSeconds / secsOfHour);
return;
} else {
this.scope.remainingHours = Math.floor(remainingSeconds / secsOfHour);
remainingSeconds -= this.scope.remainingHours * secsOfHour;
}
}
if (this.scope.countdownMinutes) {
if (this.isMinutesLastUnit()) {
this.scope.remainingMinutes = Math.ceil(
remainingSeconds / secsOfMinute,
);
return;
} else {
this.scope.remainingMinutes = Math.floor(
remainingSeconds / secsOfMinute,
);
remainingSeconds -= this.scope.remainingMinutes * secsOfMinute;
}
}
this.scope.remainingSeconds = Math.ceil(remainingSeconds);
}
protected resetCountdown() {
this.scope.remainingDays = 0;
this.scope.remainingHours = 0;
this.scope.remainingMinutes = 0;
this.scope.remainingSeconds = 0;
}
//returns remaining seconds until this.scope.time is reached.
protected getRemainingSeconds(): number {
return Math.ceil(
(Date.parse(this.scope.time) - new Date().getTime()) / 1000,
);
}
protected isYearsLastUnit(): boolean {
return (
this.scope.countdownYears == false &&
this.scope.countdownHours == false &&
this.scope.countdownMinutes == false &&
this.scope.countdownSeconds == false
);
}
protected isDaysLastUnit(): boolean {
return (
this.scope.countdownHours == false &&
this.scope.countdownMinutes == false &&
this.scope.countdownSeconds == false
);
}
protected isHoursLastUnit(): boolean {
return (
this.scope.countdownMinutes == false &&
this.scope.countdownSeconds == false
);
}
protected isMinutesLastUnit(): boolean {
return this.scope.countdownSeconds == false;
}
protected startCountdownInterval() {
this.scope.countdownRunning = false;
//call function immediatly to remove initial delay
this.updateCountdown();
//create interval and save id to clear later to prevent resource leaking
this.updateIntervalId = window.setInterval(() => {
this.updateCountdown();
}, 1000);
}
protected cancelCountdownInterval() {
this.scope.countdownRunning = false;
if (this.updateIntervalId) window.clearInterval(this.updateIntervalId);
this.updateIntervalId = undefined;
}
// deconstruction
protected disconnectedCallback() {
super.disconnectedCallback();
this.cancelCountdownInterval();
}
protected template(): ReturnType<TemplateFunction> {
return null;
}
}