Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] Update dependencies, add prettier/eslint for consistent style, add more type safety #127

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { CalendarSettings } from 'model'

export const GITHUB_GREEN = ['#c6e48b', '#7bc96f', '#49af5d', '#2e8840', '#196127']
export const DEFAULT_SETTINGS: CalendarSettings = {
year: new Date().getFullYear(),
colors: { default: GITHUB_GREEN },
entries: [{ date: '1900-01-01', color: '#7bc96f', intensity: 5, content: '' }],
showCurrentDayBorder: true,
defaultEntryIntensity: 4,
intensityScaleStart: 1,
intensityScaleEnd: 5,
weekStartDay: 1,
}
144 changes: 48 additions & 96 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,74 +1,41 @@
import { DEFAULT_SETTINGS } from 'defaults'
import { Box, CalendarData, CalendarSettings, Entry } from 'model'
import { Plugin } from 'obsidian'
import HeatmapCalendarSettingsTab from 'settings'

interface CalendarData {
year: number
colors:
| {
[index: string | number]: string[]
}
| string
entries: Entry[]
showCurrentDayBorder: boolean
defaultEntryIntensity: number
intensityScaleStart: number
intensityScaleEnd: number
}

interface CalendarSettings extends CalendarData {
colors: {
[index: string | number]: string[]
declare global {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding the global declare removes the need for ts-ignore when accessing the window object

interface Window {
renderHeatmapCalendar: (el: HTMLElement, calendarData: CalendarData) => void
}
weekStartDay: number
}

interface Entry {
date: string
intensity?: number
color: string
content: string
}
const DEFAULT_SETTINGS: CalendarSettings = {
year: new Date().getFullYear(),
colors: {
default: ['#c6e48b', '#7bc96f', '#49af5d', '#2e8840', '#196127'],
},
entries: [{ date: '1900-01-01', color: '#7bc96f', intensity: 5, content: '' }],
showCurrentDayBorder: true,
defaultEntryIntensity: 4,
intensityScaleStart: 1,
intensityScaleEnd: 5,
weekStartDay: 1,
}
export default class HeatmapCalendar extends Plugin {
settings: CalendarSettings
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing this since it is not needed, and this way we can enable strictPropertyInitialization in tsconfig


/**
* Returns a number representing how many days into the year the supplied date is.
* Example: first of january is 1, third of february is 34 (31+3)
* @param date
*/

getHowManyDaysIntoYear(date: Date): number {
const currentUTC = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())
const firstDayOfYear = Date.UTC(date.getUTCFullYear(), 0, 0)
const dayInMillis = 24 / 60 / 60 / 1000
return (currentUTC - firstDayOfYear) / dayInMillis
}

getHowManyDaysIntoYearLocal(date: Date): number {
const currentUTC = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())
const firstDayOfYear = Date.UTC(date.getFullYear(), 0, 0)
const dayInMillis = 24 / 60 / 60 / 1000
return (currentUTC - firstDayOfYear) / dayInMillis
}

/**
* Removes HTMLElements passed as entry.content and outside of the displayed year from rendering above the calendar
*/
removeHtmlElementsNotInYear(entries: Entry[], year: number) {
const calEntriesNotInDisplayedYear =
entries.filter((e) => new Date(e.date).getFullYear() !== year) ?? this.settings.entries
//@ts-ignore
calEntriesNotInDisplayedYear.forEach((e) => e.content instanceof HTMLElement && e.content.remove())
removeHtmlElementsNotInYear(entries: Entry[], year: number): void {
entries
.filter((e: Entry) => new Date(e.date).getFullYear() !== year)
.forEach((e: Entry) => e.content instanceof HTMLElement && e.content.remove())
}

clamp(input: number, min: number, max: number): number {
Expand All @@ -80,52 +47,54 @@ export default class HeatmapCalendar extends Plugin {
return this.clamp(mapped, outMin, outMax)
}

getWeekdayShort(dayNumber: number): string {
return new Date(1970, 0, dayNumber + this.settings.weekStartDay + 4).toLocaleDateString('en-US', {
getWeekdayShort(weekStartDay: number, dayNumber: number): string {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add parameter to avoid accessing a global variable

return new Date(1970, 0, dayNumber + weekStartDay + 4).toLocaleDateString('en-US', {
weekday: 'short',
})
}

async onload() {
await this.loadSettings()
async onload(): Promise<void> {
const settings = await this.loadSettings()
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

settings is only used here - no need for a global variable.


this.addSettingTab(new HeatmapCalendarSettingsTab(this.app, this))
this.addSettingTab(new HeatmapCalendarSettingsTab(this.app, this, settings))

//@ts-ignore
window.renderHeatmapCalendar = (el: HTMLElement, calendarData: CalendarData): void => {
const year = calendarData.year ?? this.settings.year
const year = calendarData.year ?? settings.year
const colors =
typeof calendarData.colors === 'string'
? this.settings.colors[calendarData.colors]
? { [calendarData.colors]: this.settings.colors[calendarData.colors] }
: this.settings.colors
: calendarData.colors ?? this.settings.colors
? settings.colors[calendarData.colors]
? { [calendarData.colors]: settings.colors[calendarData.colors] }
: settings.colors
: calendarData.colors ?? settings.colors

this.removeHtmlElementsNotInYear(calendarData.entries, year)

const calEntries =
calendarData.entries.filter((e) => new Date(e.date + 'T00:00').getFullYear() === year) ??
this.settings.entries
calendarData.entries.filter((e: Entry) => new Date(e.date + 'T00:00').getFullYear() === year) ??
settings.entries

const showCurrentDayBorder = calendarData.showCurrentDayBorder ?? settings.showCurrentDayBorder

const showCurrentDayBorder = calendarData.showCurrentDayBorder ?? this.settings.showCurrentDayBorder
const defaultEntryIntensity = calendarData.defaultEntryIntensity ?? settings.defaultEntryIntensity

const defaultEntryIntensity = calendarData.defaultEntryIntensity ?? this.settings.defaultEntryIntensity
const intensities = calEntries
.map((e: Entry) => e.intensity)
.filter((intensity): intensity is number => intensity !== undefined)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a neat trick - to type filters for undefined. intensities with this has type number[] inferred by the compiler


const intensities = calEntries.filter((e) => e.intensity).map((e) => e.intensity as number)
const minimumIntensity = intensities.length ? Math.min(...intensities) : this.settings.intensityScaleStart
const maximumIntensity = intensities.length ? Math.max(...intensities) : this.settings.intensityScaleEnd
const minimumIntensity = intensities.length ? Math.min(...intensities) : settings.intensityScaleStart
const maximumIntensity = intensities.length ? Math.max(...intensities) : settings.intensityScaleEnd
const intensityScaleStart = calendarData.intensityScaleStart ?? minimumIntensity
const intensityScaleEnd = calendarData.intensityScaleEnd ?? maximumIntensity

const mappedEntries: Entry[] = []
calEntries.forEach((e) => {
calEntries.forEach((e: Entry) => {
const newEntry = {
intensity: defaultEntryIntensity,
...e,
}
const colorIntensities =
typeof colors === 'string'
? this.settings.colors[colors]
? settings.colors[colors]
: colors[e.color] ?? colors[Object.keys(colors)[0]]

const numOfColorIntensities = Object.keys(colorIntensities).length
Expand All @@ -142,14 +111,7 @@ export default class HeatmapCalendar extends Plugin {
})

const firstDayOfYear = new Date(Date.UTC(year, 0, 1))
let numberOfEmptyDaysBeforeYearBegins = (firstDayOfYear.getUTCDay() + 7 - this.settings.weekStartDay) % 7

interface Box {
backgroundColor?: string
date?: string
content?: string
classNames?: string[]
}
let numberOfEmptyDaysBeforeYearBegins = (firstDayOfYear.getUTCDay() + 7 - settings.weekStartDay) % 7

const boxes: Array<Box> = []

Expand All @@ -162,9 +124,7 @@ export default class HeatmapCalendar extends Plugin {
const todaysDayNumberLocal = this.getHowManyDaysIntoYearLocal(new Date())

for (let day = 1; day <= numberOfDaysInYear; day++) {
const box: Box = {
classNames: [],
}
const box: Box = { classNames: [] }

// determine the date and month for the current box
const currentDate = new Date(year, 0, day)
Expand All @@ -182,10 +142,10 @@ export default class HeatmapCalendar extends Plugin {

box.date = entry.date

if (entry.content) box.content = entry.content
if (entry.content !== undefined) box.content = entry.content.toString()

const currentDayColors = entry.color ? colors[entry.color] : colors[Object.keys(colors)[0]]
box.backgroundColor = currentDayColors[(entry.intensity as number) - 1]
if (entry.intensity !== undefined) box.backgroundColor = currentDayColors[entry.intensity - 1]
} else box.classNames?.push('isEmpty')
boxes.push(box)
}
Expand All @@ -206,25 +166,16 @@ export default class HeatmapCalendar extends Plugin {
parent: heatmapCalendarGraphDiv,
})

createEl('li', { text: 'Jan', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'Feb', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'Mar', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'Apr', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'May', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'Jun', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'Jul', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'Aug', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'Sep', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'Oct', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'Nov', parent: heatmapCalendarMonthsUl })
createEl('li', { text: 'Dec', parent: heatmapCalendarMonthsUl })
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simplifies the previous duplication

months.forEach((month: string) => createEl('li', { text: month, parent: heatmapCalendarMonthsUl }))

const heatmapCalendarDaysUl = createEl('ul', {
cls: 'heatmap-calendar-days',
parent: heatmapCalendarGraphDiv,
})

for (let i = 0; i < 7; i++) createEl('li', { text: this.getWeekdayShort(i), parent: heatmapCalendarDaysUl })
for (let i = 0; i < 7; i++)
createEl('li', { text: this.getWeekdayShort(settings.weekStartDay, i), parent: heatmapCalendarDaysUl })

const heatmapCalendarBoxesUl = createEl('ul', {
cls: 'heatmap-calendar-boxes',
Expand All @@ -250,14 +201,15 @@ export default class HeatmapCalendar extends Plugin {
}
}

onunload() {}
onunload(): void {}

async loadSettings() {
console.log('heyoh', await this.loadData())
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData())
async loadSettings(): Promise<CalendarSettings> {
const settings: CalendarSettings = await this.loadData()
console.log('heyoh', settings)
return Object.assign({}, DEFAULT_SETTINGS, settings)
}

async saveSettings() {
await this.saveData(this.settings)
async saveSettings(settings: CalendarSettings): Promise<void> {
await this.saveData(settings)
}
}
40 changes: 40 additions & 0 deletions src/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export interface CalendarData {
year: number
colors:
| {
[index: string | number]: string[]
}
| string
entries: Entry[]
showCurrentDayBorder: boolean
defaultEntryIntensity: number
intensityScaleStart: number
intensityScaleEnd: number
}

export interface CalendarSettings extends CalendarData {
colors: {
[index: string | number]: string[]
}
weekStartDay: number
}

export interface Intensity {
default: number
startScale: number
endScale: number
}

export interface Entry {
date: string
intensity?: number
color: string
content: string | HTMLElement
}

export interface Box {
backgroundColor?: string
date?: string
content?: string
classNames?: string[]
}
25 changes: 14 additions & 11 deletions src/settings.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import HeatmapCalendar from 'main'
import { CalendarSettings } from 'model'
import { App, PluginSettingTab, setIcon, Setting } from 'obsidian'

export default class HeatmapCalendarSettingsTab extends PluginSettingTab {
plugin: HeatmapCalendar
settings: CalendarSettings

constructor(app: App, plugin: HeatmapCalendar) {
constructor(app: App, plugin: HeatmapCalendar, settings: CalendarSettings) {
super(app, plugin)
this.plugin = plugin
this.settings = settings
}

private async addColorMap(color: { key: string; value: string }) {
Expand All @@ -19,20 +22,20 @@ export default class HeatmapCalendarSettingsTab extends PluginSettingTab {
if (!validatedArray) isValid.value = false

if (isValid.key && isValid.value) {
this.plugin.settings.colors[color.key] = validatedArray as string[]
this.settings.colors[color.key] = validatedArray as string[]

await this.plugin.saveSettings()
await this.plugin.saveSettings(this.settings)

this.display()
}

return isValid
}

private async deleteColorMap(key: keyof typeof this.plugin.settings.colors) {
delete this.plugin.settings.colors[key]
private async deleteColorMap(key: keyof typeof this.settings.colors) {
delete this.settings.colors[key]

await this.plugin.saveSettings()
await this.plugin.saveSettings(this.settings)

this.display()
}
Expand All @@ -43,7 +46,7 @@ export default class HeatmapCalendarSettingsTab extends PluginSettingTab {
containerEl.createEl('h3', { text: 'Colors' })
this.displayColorHelp(containerEl)

for (const [key, colors] of Object.entries(this.plugin.settings.colors)) {
for (const [key, colors] of Object.entries(this.settings.colors)) {
const colorEntryContainer = containerEl.createDiv({
cls: 'heatmap-calendar-settings-colors__container',
})
Expand Down Expand Up @@ -164,10 +167,10 @@ export default class HeatmapCalendarSettingsTab extends PluginSettingTab {
5: 'Friday',
6: 'Saturday',
})
.setValue(this.plugin.settings.weekStartDay.toString())
.setValue(this.settings.weekStartDay.toString())
.onChange(async (value) => {
this.plugin.settings.weekStartDay = +value
await this.plugin.saveSettings()
this.settings.weekStartDay = +value
await this.plugin.saveSettings(this.settings)
})
)
}
Expand All @@ -183,6 +186,6 @@ export default class HeatmapCalendarSettingsTab extends PluginSettingTab {

this.displayColorSettings()

console.log('settings', this.plugin.settings)
console.log('settings', this.settings)
}
}
Loading