From a936de4285da20e2241a21bb7978a6e7acd650b4 Mon Sep 17 00:00:00 2001 From: Franco Speziali Date: Tue, 14 Jun 2022 18:40:47 +0200 Subject: [PATCH] Added option for grouping by year (for handling events that span multiple years) --- README.md | 7 ++-- src/Calendar/index.ts | 18 ++++++++-- src/EventGrouping/YearGrouping.ts | 42 ++++++++++++++++++++++ src/EventGrouping/index.ts | 3 +- src/HtmlRenderer/Year/GroupYearRenderer.ts | 30 ++++++++++++++++ src/HtmlRenderer/Year/index.ts | 19 ++++++++++ src/HtmlRenderer/index.ts | 9 ++--- src/types.ts | 1 + 8 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 src/EventGrouping/YearGrouping.ts create mode 100644 src/HtmlRenderer/Year/GroupYearRenderer.ts create mode 100644 src/HtmlRenderer/Year/index.ts diff --git a/README.md b/README.md index 515b77e..f0fefab 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This is a plugin for the [Joplin](https://joplinapp.org/) note-taking app. It creates a calendar view of events that have been specified using the [YAML](https://yaml.org/) syntax within a fenced block. -- Organises events into "groupings"; you can group events by **day**, **week** or by **month** +- Organises events into "groupings"; you can group events by **day**, **week**, **month** or **year** - Focus is on readability and simplicity of content, which is why the **YAML** is simple in structure ###### Example Day view @@ -13,10 +13,11 @@ It creates a calendar view of events that have been specified using the [YAML](h ## Features -- A grouping that contains no events will show the **date** (day grouping), **week number** (week grouping), or **month** (month grouping) +- A grouping that contains no events will show the **date** (day grouping), **week number** (week grouping), **month** (month grouping), or **year** (year grouping) - If a grouping falls under the **current date**, it is highlighted in yellow - Events can be specified in **any order** - Events can be assigned an **icon** 🎁 such as an emoji +- Events without an icon will use the first 2 characters from the title ## How to use @@ -40,7 +41,7 @@ Events are specified using the YAML syntax, with the following keys: > Sets the grouping for the view > - _**optional**_ > - **default** `day` -> - **accepts** : `day`, `week`, `month`, `d`, `w`, `m` +> - **accepts** : `day`, `week`, `month`, `year`, `d`, `w`, `m`, `y` ### events: > Each individual event is a list with its own properties diff --git a/src/Calendar/index.ts b/src/Calendar/index.ts index e7575a5..9786ab4 100644 --- a/src/Calendar/index.ts +++ b/src/Calendar/index.ts @@ -1,7 +1,17 @@ import { GroupTypes } from "../types"; import Events from "../Events/"; -import { DayGrouping, WeekGrouping, MonthGrouping } from "../EventGrouping"; -import { DayRenderer, WeekRenderer, MonthRenderer } from "../HtmlRenderer"; +import { + DayGrouping, + MonthGrouping, + WeekGrouping, + YearGrouping, +} from "../EventGrouping"; +import { + DayRenderer, + MonthRenderer, + WeekRenderer, + YearRenderer, +} from "../HtmlRenderer"; export default class Calendar { public readonly jsonContent: object; @@ -30,6 +40,10 @@ export default class Calendar { return new MonthRenderer( new MonthGrouping(this.events.sortedEvents) ).render(); + case GroupTypes.Year: + return new YearRenderer( + new YearGrouping(this.events.sortedEvents) + ).render(); } } } diff --git a/src/EventGrouping/YearGrouping.ts b/src/EventGrouping/YearGrouping.ts new file mode 100644 index 0000000..c7c5a8a --- /dev/null +++ b/src/EventGrouping/YearGrouping.ts @@ -0,0 +1,42 @@ +import EventGrouping from "./EventGrouping"; +import { Event, Groups } from "../types"; +import { + add, + differenceInCalendarYears, + isThisYear as dateFnsIsThisYear, +} from "date-fns"; + +export default class YearGrouping extends EventGrouping { + static isThisYear(date: Date): boolean { + return dateFnsIsThisYear(date); + } + + constructor(sortedEvents: Event[]) { + super(sortedEvents); + this.groups = this.group(); + } + + group(): Groups { + const numberOfGroups = differenceInCalendarYears( + this.lastEvent.date, + this.firstEvent.date + ); + + return this.sortedEvents.reduce((prev, event) => { + const groupNumber = differenceInCalendarYears( + event.date, + this.firstEvent.date + ); + + prev[groupNumber].push(event); + + return prev; + }, this.generateEmptyGroups(numberOfGroups)); + } + + public getDateFromGroupIndex(index: number): Date { + const startDate = this.firstEvent.date; + + return add(startDate, { years: index }); + } +} diff --git a/src/EventGrouping/index.ts b/src/EventGrouping/index.ts index a1874ba..30ecee1 100644 --- a/src/EventGrouping/index.ts +++ b/src/EventGrouping/index.ts @@ -1,5 +1,6 @@ import DayGrouping from "./DayGrouping"; import WeekGrouping from "./WeekGrouping"; import MonthGrouping from "./MonthGrouping"; +import YearGrouping from "./YearGrouping"; -export { DayGrouping, WeekGrouping, MonthGrouping }; +export { DayGrouping, WeekGrouping, MonthGrouping, YearGrouping }; diff --git a/src/HtmlRenderer/Year/GroupYearRenderer.ts b/src/HtmlRenderer/Year/GroupYearRenderer.ts new file mode 100644 index 0000000..19dfaad --- /dev/null +++ b/src/HtmlRenderer/Year/GroupYearRenderer.ts @@ -0,0 +1,30 @@ +import { Event } from "../../types"; +import { YearGrouping } from "../../EventGrouping"; + +import GroupRenderer from "../Abstract/GroupRenderer"; + +export default class GroupYearRenderer extends GroupRenderer { + constructor(group: Event[], groupDate: Date, groupIndex: number) { + super(group, groupDate, groupIndex); + + if (YearGrouping.isThisYear(groupDate)) { + this.highlightGroup(); + } + } + + protected renderDateAsIcon(eventDate, groupIndex): HTMLSpanElement { + const html = document.createElement("span"); + html.className = "icon"; + + html.textContent = eventDate.toLocaleDateString(undefined, { + year: "numeric", + }); + html.className += " primary"; + + if (YearGrouping.isThisYear(eventDate)) { + html.className += " highlighted"; + } + + return html; + } +} diff --git a/src/HtmlRenderer/Year/index.ts b/src/HtmlRenderer/Year/index.ts new file mode 100644 index 0000000..e7bb6bf --- /dev/null +++ b/src/HtmlRenderer/Year/index.ts @@ -0,0 +1,19 @@ +import { Event } from "../../types"; +import { YearGrouping } from "../../EventGrouping"; +import Renderer from "../Abstract/Renderer"; +import GroupYearRenderer from "./GroupYearRenderer"; + +export default class YearRenderer extends Renderer { + constructor(eventGrouping: YearGrouping) { + super(eventGrouping); + this.container.className += " scale-year"; + } + + protected renderGroups( + group: Event[], + groupDate: Date, + groupIndex: number + ): HTMLDivElement { + return new GroupYearRenderer(group, groupDate, groupIndex).render(); + } +} diff --git a/src/HtmlRenderer/index.ts b/src/HtmlRenderer/index.ts index ceff40f..8e96693 100644 --- a/src/HtmlRenderer/index.ts +++ b/src/HtmlRenderer/index.ts @@ -1,5 +1,6 @@ -import DayRenderer from "./Day/"; -import WeekRenderer from "./Week/"; -import MonthRenderer from "./Month/"; +import DayRenderer from "./Day"; +import WeekRenderer from "./Week"; +import MonthRenderer from "./Month"; +import YearRenderer from "./Year"; -export { DayRenderer, WeekRenderer, MonthRenderer }; +export { DayRenderer, WeekRenderer, MonthRenderer, YearRenderer }; diff --git a/src/types.ts b/src/types.ts index a550dc4..44fe5fe 100644 --- a/src/types.ts +++ b/src/types.ts @@ -2,6 +2,7 @@ export enum GroupTypes { Day = "D", Week = "W", Month = "M", + Year = "Y", } export interface Event {