Refactored entire rendering module; added new feature for "highlighting" a group, when the current date falls within that group
This commit is contained in:
parent
024abebf2a
commit
da62052e79
@ -1,5 +1,7 @@
|
|||||||
|
import { Event } from "../types";
|
||||||
import { DayGrouping } from "../EventGrouping";
|
import { DayGrouping } from "../EventGrouping";
|
||||||
import Renderer from "./Renderer";
|
import Renderer from "./Renderer";
|
||||||
|
import GroupDayRenderer from "./GroupRenderer/GroupDayRenderer";
|
||||||
|
|
||||||
export default class DayRenderer extends Renderer {
|
export default class DayRenderer extends Renderer {
|
||||||
constructor(eventGrouping: DayGrouping) {
|
constructor(eventGrouping: DayGrouping) {
|
||||||
@ -7,21 +9,11 @@ export default class DayRenderer extends Renderer {
|
|||||||
this.container.className += " scale-day";
|
this.container.className += " scale-day";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderDateAsIcon(eventDate, groupIndex): HTMLSpanElement {
|
protected renderGroups(
|
||||||
const html = document.createElement("span");
|
group: Event[],
|
||||||
html.className = "icon";
|
groupDate: Date,
|
||||||
|
groupIndex: number
|
||||||
const dayInMonth = eventDate.getDate();
|
): HTMLDivElement {
|
||||||
|
return new GroupDayRenderer(group, groupDate, groupIndex).render();
|
||||||
if (dayInMonth === 1) {
|
|
||||||
html.textContent = eventDate
|
|
||||||
.toLocaleDateString(undefined, { month: "long" })
|
|
||||||
.slice(0, 3);
|
|
||||||
html.className += " primary";
|
|
||||||
} else {
|
|
||||||
html.textContent = dayInMonth.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { Event } from "../../types";
|
||||||
|
import { DayGrouping } from "../../EventGrouping";
|
||||||
|
|
||||||
|
import GroupRenderer from "./GroupRenderer";
|
||||||
|
|
||||||
|
export default class GroupDayRenderer extends GroupRenderer {
|
||||||
|
constructor(group: Event[], groupDate: Date, groupIndex: number) {
|
||||||
|
super(group, groupDate, groupIndex);
|
||||||
|
|
||||||
|
if (DayGrouping.isToday(groupDate)) {
|
||||||
|
this.highlightGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderDateAsIcon(eventDate, groupIndex): HTMLSpanElement {
|
||||||
|
const html = document.createElement("span");
|
||||||
|
html.className = "icon";
|
||||||
|
|
||||||
|
const dayInMonth = eventDate.getDate();
|
||||||
|
|
||||||
|
if (dayInMonth === 1) {
|
||||||
|
html.textContent = eventDate
|
||||||
|
.toLocaleDateString(undefined, { month: "long" })
|
||||||
|
.slice(0, 3);
|
||||||
|
html.className += " primary";
|
||||||
|
} else {
|
||||||
|
html.textContent = dayInMonth.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { Event } from "../../types";
|
||||||
|
import { MonthGrouping } from "../../EventGrouping";
|
||||||
|
|
||||||
|
import GroupRenderer from "./GroupRenderer";
|
||||||
|
|
||||||
|
export default class GroupMonthRenderer extends GroupRenderer {
|
||||||
|
constructor(group: Event[], groupDate: Date, groupIndex: number) {
|
||||||
|
super(group, groupDate, groupIndex);
|
||||||
|
|
||||||
|
if (MonthGrouping.isThisMonth(groupDate)) {
|
||||||
|
this.highlightGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderDateAsIcon(eventDate, groupIndex): HTMLSpanElement {
|
||||||
|
const html = document.createElement("span");
|
||||||
|
html.className = "icon";
|
||||||
|
|
||||||
|
// unlike getDate(), getMonth is 0 based
|
||||||
|
const month = eventDate.getMonth() + 1;
|
||||||
|
|
||||||
|
if (month === 1) {
|
||||||
|
html.textContent = eventDate.toLocaleDateString(undefined, {
|
||||||
|
year: "numeric",
|
||||||
|
});
|
||||||
|
html.className += " primary";
|
||||||
|
} else {
|
||||||
|
html.textContent = eventDate
|
||||||
|
.toLocaleDateString(undefined, {
|
||||||
|
month: "long",
|
||||||
|
})
|
||||||
|
.slice(0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
108
src/EventGroupingHtmlRenderer/GroupRenderer/GroupRenderer.ts
Normal file
108
src/EventGroupingHtmlRenderer/GroupRenderer/GroupRenderer.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { Event } from "../../types";
|
||||||
|
import { generateRandomColor } from "../../utilities";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
|
||||||
|
export default abstract class GroupRenderer {
|
||||||
|
protected container: HTMLDivElement;
|
||||||
|
protected group: Event[];
|
||||||
|
protected groupDate: Date;
|
||||||
|
protected groupIndex: number;
|
||||||
|
|
||||||
|
protected abstract renderDateAsIcon(
|
||||||
|
eventDate: Date,
|
||||||
|
groupIndex: number
|
||||||
|
): HTMLSpanElement;
|
||||||
|
|
||||||
|
constructor(group: Event[], groupDate: Date, groupIndex: number) {
|
||||||
|
this.container = document.createElement("div");
|
||||||
|
this.container.className = "group";
|
||||||
|
this.group = group;
|
||||||
|
this.groupDate = groupDate;
|
||||||
|
this.groupIndex = groupIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightGroup(): void {
|
||||||
|
this.container.className += " highlight";
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): HTMLDivElement {
|
||||||
|
if (this.group.length) {
|
||||||
|
const htmlHoverCard = this.renderHoverCard(this.group);
|
||||||
|
this.container.appendChild(htmlHoverCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
let htmlGroupIcon: HTMLSpanElement;
|
||||||
|
// only need to render icon from first event in group
|
||||||
|
const event = this.group[0];
|
||||||
|
|
||||||
|
if (event) {
|
||||||
|
htmlGroupIcon = this.renderIcon(event);
|
||||||
|
this.container.className += " icon";
|
||||||
|
if (event.bgColor) {
|
||||||
|
this.container.style.backgroundColor = event.bgColor;
|
||||||
|
} else {
|
||||||
|
const randomColor = generateRandomColor();
|
||||||
|
this.container.style.backgroundColor = `rgb(${randomColor.r}, ${randomColor.g}, ${randomColor.b})`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
htmlGroupIcon = this.renderDateAsIcon(this.groupDate, this.groupIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.container.appendChild(htmlGroupIcon);
|
||||||
|
|
||||||
|
return this.container;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderIcon(event: Event | undefined): HTMLSpanElement {
|
||||||
|
const html = document.createElement("span");
|
||||||
|
html.className = "event";
|
||||||
|
|
||||||
|
if (event.icon) {
|
||||||
|
html.textContent = event.icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHoverCard(events: Event[]): HTMLDivElement {
|
||||||
|
const html = document.createElement("div");
|
||||||
|
html.className = "hover-card";
|
||||||
|
|
||||||
|
const htmlMonthYear = document.createElement("p");
|
||||||
|
htmlMonthYear.className = "month-year";
|
||||||
|
htmlMonthYear.textContent = events[0].date.toLocaleDateString(undefined, {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
});
|
||||||
|
|
||||||
|
html.appendChild(htmlMonthYear);
|
||||||
|
|
||||||
|
const cardDetails = events.map((event) => {
|
||||||
|
if (!event) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const htmlEvent = document.createElement("div");
|
||||||
|
htmlEvent.className = "event";
|
||||||
|
const htmlEventDate = document.createElement("p");
|
||||||
|
htmlEventDate.className = "date";
|
||||||
|
htmlEventDate.textContent = format(event.date, "EEEE do");
|
||||||
|
const htmlEventTitle = document.createElement("p");
|
||||||
|
htmlEventTitle.className = "title";
|
||||||
|
htmlEventTitle.textContent = event.title;
|
||||||
|
const htmlEventText = document.createElement("p");
|
||||||
|
htmlEventText.className = "text";
|
||||||
|
htmlEventText.textContent = event.text;
|
||||||
|
|
||||||
|
htmlEvent.appendChild(htmlEventDate);
|
||||||
|
htmlEvent.appendChild(htmlEventTitle);
|
||||||
|
htmlEvent.appendChild(htmlEventText);
|
||||||
|
|
||||||
|
return htmlEvent;
|
||||||
|
});
|
||||||
|
|
||||||
|
cardDetails.forEach((cardDetail) => html.appendChild(cardDetail));
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { Event } from "../../types";
|
||||||
|
import { WeekGrouping } from "../../EventGrouping";
|
||||||
|
|
||||||
|
import GroupRenderer from "./GroupRenderer";
|
||||||
|
|
||||||
|
export default class GroupWeekRenderer extends GroupRenderer {
|
||||||
|
constructor(group: Event[], groupDate: Date, groupIndex: number) {
|
||||||
|
super(group, groupDate, groupIndex);
|
||||||
|
|
||||||
|
if (WeekGrouping.isThisWeek(groupDate)) {
|
||||||
|
this.highlightGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderDateAsIcon(eventDate, groupIndex): HTMLSpanElement {
|
||||||
|
const html = document.createElement("span");
|
||||||
|
html.className = "icon";
|
||||||
|
|
||||||
|
const week = WeekGrouping.getWeek(eventDate);
|
||||||
|
|
||||||
|
if (week === 1) {
|
||||||
|
html.textContent = eventDate.toLocaleDateString(undefined, {
|
||||||
|
year: "numeric",
|
||||||
|
});
|
||||||
|
html.className += " primary";
|
||||||
|
} else {
|
||||||
|
html.textContent = week.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WeekGrouping.isThisWeek(eventDate)) {
|
||||||
|
html.className += " highlighted";
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
|
import { Event } from "../types";
|
||||||
import { MonthGrouping } from "../EventGrouping";
|
import { MonthGrouping } from "../EventGrouping";
|
||||||
import Renderer from "./Renderer";
|
import Renderer from "./Renderer";
|
||||||
|
import GroupMonthRenderer from "./GroupRenderer/GroupMonthRenderer";
|
||||||
|
|
||||||
export default class MonthRenderer extends Renderer {
|
export default class MonthRenderer extends Renderer {
|
||||||
constructor(eventGrouping: MonthGrouping) {
|
constructor(eventGrouping: MonthGrouping) {
|
||||||
@ -7,26 +9,13 @@ export default class MonthRenderer extends Renderer {
|
|||||||
this.container.className += " scale-month";
|
this.container.className += " scale-month";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderDateAsIcon(eventDate, groupIndex): HTMLSpanElement {
|
protected renderGroups(
|
||||||
const html = document.createElement("span");
|
group: Event[],
|
||||||
html.className = "icon";
|
groupDate: Date,
|
||||||
|
groupIndex: number
|
||||||
|
): HTMLDivElement {
|
||||||
|
const monthGroup = new GroupMonthRenderer(group, groupDate, groupIndex);
|
||||||
|
|
||||||
// unlike getDate(), getMonth is 0 based
|
return monthGroup.render();
|
||||||
const month = eventDate.getMonth() + 1;
|
|
||||||
|
|
||||||
if (month === 1) {
|
|
||||||
html.textContent = eventDate.toLocaleDateString(undefined, {
|
|
||||||
year: "numeric",
|
|
||||||
});
|
|
||||||
html.className += " primary";
|
|
||||||
} else {
|
|
||||||
html.textContent = eventDate
|
|
||||||
.toLocaleDateString(undefined, {
|
|
||||||
month: "long",
|
|
||||||
})
|
|
||||||
.slice(0, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
import { format } from "date-fns";
|
|
||||||
import { generateRandomColor } from "../utilities";
|
|
||||||
import EventGrouping from "../EventGrouping/EventGrouping";
|
import EventGrouping from "../EventGrouping/EventGrouping";
|
||||||
import { Event } from "../types";
|
import { Event } from "../types";
|
||||||
|
|
||||||
export default abstract class Renderer {
|
export default abstract class Renderer {
|
||||||
protected abstract renderDateAsIcon(
|
protected abstract renderGroups(
|
||||||
eventDate: Date,
|
group: Event[],
|
||||||
|
groupDate: Date,
|
||||||
groupIndex: number
|
groupIndex: number
|
||||||
): HTMLSpanElement;
|
): HTMLDivElement;
|
||||||
|
|
||||||
protected container: HTMLDivElement;
|
protected container: HTMLDivElement;
|
||||||
protected eventGrouping: EventGrouping;
|
protected eventGrouping: EventGrouping;
|
||||||
@ -18,95 +17,16 @@ export default abstract class Renderer {
|
|||||||
this.eventGrouping = eventGrouping;
|
this.eventGrouping = eventGrouping;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderGroups(): HTMLDivElement[] {
|
protected _renderGroups(): HTMLDivElement[] {
|
||||||
return this.eventGrouping.groups.map((group, index) => {
|
return this.eventGrouping.groups.map((group: Event[], index: number) => {
|
||||||
const html = document.createElement("div");
|
const groupDate = this.eventGrouping.getDateFromGroupIndex(index);
|
||||||
html.className = "group";
|
|
||||||
|
|
||||||
if (group.length) {
|
return this.renderGroups(group, groupDate, index);
|
||||||
const htmlHoverCard = this.renderHoverCard(group);
|
|
||||||
html.appendChild(htmlHoverCard);
|
|
||||||
}
|
|
||||||
|
|
||||||
let htmlGroupIcon: HTMLSpanElement;
|
|
||||||
// only need to render icon from first event in group
|
|
||||||
const event = group[0];
|
|
||||||
|
|
||||||
if (event) {
|
|
||||||
htmlGroupIcon = this.renderIcon(event);
|
|
||||||
html.className += " icon";
|
|
||||||
if (event.bgColor) {
|
|
||||||
html.style.backgroundColor = event.bgColor;
|
|
||||||
} else {
|
|
||||||
const randomColor = generateRandomColor();
|
|
||||||
html.style.backgroundColor = `rgb(${randomColor.r}, ${randomColor.g}, ${randomColor.b})`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const eventDate = this.eventGrouping.getDateFromGroupIndex(index);
|
|
||||||
htmlGroupIcon = this.renderDateAsIcon(eventDate, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
html.appendChild(htmlGroupIcon);
|
|
||||||
|
|
||||||
return html;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderHoverCard(events: Event[]): HTMLDivElement {
|
|
||||||
const html = document.createElement("div");
|
|
||||||
html.className = "hover-card";
|
|
||||||
|
|
||||||
const htmlMonthYear = document.createElement("p");
|
|
||||||
htmlMonthYear.className = "month-year";
|
|
||||||
htmlMonthYear.textContent = events[0].date.toLocaleDateString(undefined, {
|
|
||||||
year: "numeric",
|
|
||||||
month: "long",
|
|
||||||
});
|
|
||||||
|
|
||||||
html.appendChild(htmlMonthYear);
|
|
||||||
|
|
||||||
const cardDetails = events.map((event) => {
|
|
||||||
if (!event) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const htmlEvent = document.createElement("div");
|
|
||||||
htmlEvent.className = "event";
|
|
||||||
const htmlEventDate = document.createElement("p");
|
|
||||||
htmlEventDate.className = "date";
|
|
||||||
htmlEventDate.textContent = format(event.date, "EEEE do");
|
|
||||||
const htmlEventTitle = document.createElement("p");
|
|
||||||
htmlEventTitle.className = "title";
|
|
||||||
htmlEventTitle.textContent = event.title;
|
|
||||||
const htmlEventText = document.createElement("p");
|
|
||||||
htmlEventText.className = "text";
|
|
||||||
htmlEventText.textContent = event.text;
|
|
||||||
|
|
||||||
htmlEvent.appendChild(htmlEventDate);
|
|
||||||
htmlEvent.appendChild(htmlEventTitle);
|
|
||||||
htmlEvent.appendChild(htmlEventText);
|
|
||||||
|
|
||||||
return htmlEvent;
|
|
||||||
});
|
|
||||||
|
|
||||||
cardDetails.forEach((cardDetail) => html.appendChild(cardDetail));
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected renderIcon(event: Event | undefined): HTMLSpanElement {
|
|
||||||
const html = document.createElement("span");
|
|
||||||
html.className = "event";
|
|
||||||
|
|
||||||
if (event.icon) {
|
|
||||||
html.textContent = event.icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
public render(): HTMLDivElement {
|
public render(): HTMLDivElement {
|
||||||
const groups = this.renderGroups();
|
const groups = this._renderGroups();
|
||||||
|
|
||||||
groups.forEach((group) => this.container.appendChild(group));
|
groups.forEach((group) => this.container.appendChild(group));
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { getWeek } from "date-fns";
|
import { Event } from "../types";
|
||||||
|
|
||||||
import { WeekGrouping } from "../EventGrouping";
|
import { WeekGrouping } from "../EventGrouping";
|
||||||
import Renderer from "./Renderer";
|
import Renderer from "./Renderer";
|
||||||
|
import GroupWeekRenderer from "./GroupRenderer/GroupWeekRenderer";
|
||||||
|
|
||||||
export default class WeekRenderer extends Renderer {
|
export default class WeekRenderer extends Renderer {
|
||||||
constructor(eventGrouping: WeekGrouping) {
|
constructor(eventGrouping: WeekGrouping) {
|
||||||
@ -9,21 +9,11 @@ export default class WeekRenderer extends Renderer {
|
|||||||
this.container.className += " scale-week";
|
this.container.className += " scale-week";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected renderDateAsIcon(eventDate, groupIndex): HTMLSpanElement {
|
protected renderGroups(
|
||||||
const html = document.createElement("span");
|
group: Event[],
|
||||||
html.className = "icon";
|
groupDate: Date,
|
||||||
|
groupIndex: number
|
||||||
const week = getWeek(eventDate);
|
): HTMLDivElement {
|
||||||
|
return new GroupWeekRenderer(group, groupDate, groupIndex).render();
|
||||||
if (week === 1) {
|
|
||||||
html.textContent = eventDate.toLocaleDateString(undefined, {
|
|
||||||
year: "numeric",
|
|
||||||
});
|
|
||||||
html.className += " primary";
|
|
||||||
} else {
|
|
||||||
html.textContent = week.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,13 +8,17 @@
|
|||||||
width: 36px;
|
width: 36px;
|
||||||
height: 26px;
|
height: 26px;
|
||||||
margin: 0 4px 4px 0;
|
margin: 0 4px 4px 0;
|
||||||
border-top: 10px solid rgba(255, 140, 140, 0.5);
|
border-top: 10px solid rgba(255, 140, 140, 1);
|
||||||
border-right: 1px solid rgba(20, 20, 20, 0.7);
|
border-right: 1px solid rgba(20, 20, 20, 0.7);
|
||||||
border-bottom: 1px solid rgba(20, 20, 20, 0.7);
|
border-bottom: 1px solid rgba(20, 20, 20, 0.7);
|
||||||
border-left: 1px solid rgba(20, 20, 20, 0.2);
|
border-left: 1px solid rgba(20, 20, 20, 0.2);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.event-calendar .group.highlight {
|
||||||
|
border-top: 10px solid rgba(255, 223, 65, 1);
|
||||||
|
}
|
||||||
|
|
||||||
.event-calendar .group .icon {
|
.event-calendar .group .icon {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user