import { Component, Attributes } from "armature"; import * as API from "./API"; import template from "./Body-template"; import GeckoCalendar from "./"; import Month from "./Month"; import EventStore from "./EventStore"; // Delete months out of the DOM that are this far away from the current focus const DELETION_THRESHOLD = 2; interface State { zeroPoint: number; } @Attributes({ tag: "gecko-calendar-body", template }) export default class Body extends Component { parent: GeckoCalendar; viewport: HTMLElement; protected months = new Map(); constructor(state: State) { super(state); } installed() { super.installed(); this.viewport = this.element.querySelector(".viewport > .months") as HTMLElement; this.addMonth(this.state.zeroPoint); this.addMonth(this.state.zeroPoint - 1); this.addMonth(this.state.zeroPoint + 1); } /** * Returns the existing Month component with the given month number */ getMonth(offset: number) { return this.months.get(offset); } /** * Adds a month to the calendar if it doesn't already exist. */ addMonth(month: number) { const existing = this.getMonth(month); if (existing) { return existing; } const component = new Month({ month, calendar: this.parent }) .setLabel(month.toString()) .setParent(this); component.reify(); const offset = month - this.state.zeroPoint; component.element.style.left = `${ offset * 100 }%`; this.months.set(month, component); this.viewport.appendChild(component.element); this.resizeViewport(); // Grab events from the API or the cache and throw them in! API.queryEvents(month).then(events => component.addEvents(events)); return component; } resizeViewport() { // We put this in an animation frame to make sure all of our sizes are correct requestAnimationFrame(() => { const maxSize = Array.from(this.viewport.children) .map(e => e.clientHeight) .reduce((a, b) => Math.max(a, b), 0); this.viewport.style.height = `${ maxSize }px`; }); } scrollTo(month: number) { const offset = month - this.state.zeroPoint; this.viewport.style.left = `${ -offset * 100 }%`; // Delete months that are DELETION_THRESHOLD or more months away const keys = Array.from(this.months.keys()); for (const key of keys) { if (key < month - DELETION_THRESHOLD || key > month + DELETION_THRESHOLD) { const month = this.months.get(key); month.remove(); this.months.delete(key); } } // Add the months immediately around this one if (!this.getMonth(month + 1)) { this.addMonth(month + 1); } if (!this.getMonth(month - 1)) { this.addMonth(month - 1); } } }