charmite/calendar.go

167 lines
4.0 KiB
Go
Raw Permalink Normal View History

2025-06-17 16:39:51 +02:00
package main
import (
"fmt"
"strings"
"time"
"github.com/charmbracelet/lipgloss"
"github.com/dustin/go-humanize"
)
type calendarTime struct {
time.Time
}
// June 2025
// Su Mo Tu We Th Fr Sa
// 1 2 3 4 5 6 7
// 8 9 10 11 12 13 14
// 15 16 17 18 19 20 21
// 22 23 24 25 26 27 28
// 29 30
var dayCols = map[int]time.Weekday{
0: time.Monday,
1: time.Tuesday,
2: time.Wednesday,
3: time.Thursday,
4: time.Friday,
5: time.Saturday,
6: time.Sunday,
}
const brailleStart = 0x2800
const brailleEnd = 0x28FF
var baseStyle = lipgloss.NewStyle().Width(7 * 3)
var weekdayStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#2222f2"))
var weekendStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#ff0022"))
var monthNameStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#00dd80"))
var yearStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#a0dd10"))
var calendarTitle = baseStyle.AlignHorizontal(lipgloss.Center)
var dayStyle = lipgloss.NewStyle()
var dayStartStyle = dayStyle.Background(lipgloss.Color("#555555"))
var highlightStyle = dayStyle.Foreground(lipgloss.Color("#000000")).Background(lipgloss.Color("#ffffff")).Blink(true)
// calendar takes three calendarTime's, the calendar month to show, the "start" calendar time
// (shown with a underline) and the highlighted calendarTime.
func calendar(t calendarTime, start calendarTime, hl calendarTime, styles map[string]lipgloss.Style) string {
f, _ := t.firstLastDays()
out := calendarTitle.Render(fmt.Sprintf("%s %s", monthNameStyle.Render(f.Month().String()), yearStyle.Render(fmt.Sprint(f.Year()))))
out += "\n"
out += weekdayStyle.Render(" Mo Tu We Th Fr ")
out += weekendStyle.Render("Sa Su")
out += "\n"
at := f
col := 0
lines := 0
for {
if at.Weekday() == dayCols[col] {
// codePoint := brailleStart + rand.Intn(brailleEnd-brailleStart+1)
// brailleChar := rune(codePoint)
// if rand.Intn(100) < 80 {
// brailleChar = ' '
// }
// pre := lipgloss.NewStyle().Foreground(lipgloss.Color("#d00010")).Render(string(brailleChar))
dayStyle := dayStyle
specialStyle, ok := styles[at.Format(time.DateOnly)]
if ok {
dayStyle = specialStyle
}
dayString := fmt.Sprintf("%2d", at.Day())
padding := 3 - len(fmt.Sprint(at.Day()))
padding = 1
out += strings.Repeat(" ", padding)
if at.dayEqual(hl) {
out += highlightStyle.Render(dayString)
} else if at.dayEqual(start) {
out += dayStartStyle.Render(dayString)
} else {
out += dayStyle.Render(dayString)
}
} else {
col++
out += " "
continue
}
at = calendarTime{at.AddDate(0, 0, 1)}
if at.Month() != f.Month() {
break
}
col++
if col > 6 {
out += "\n"
col = 0
lines++
}
}
for lines < 6 {
out += "\n"
lines++
}
out += "\n"
return out
}
func (ct calendarTime) dayEqual(t calendarTime) bool {
y1, m1, d1 := ct.Date()
y2, m2, d2 := t.Date()
if y1 == y2 && m1 == m2 && d1 == d2 {
return true
}
return false
}
func (ct calendarTime) firstLastDays() (calendarTime, calendarTime) {
loc := ct.Location()
y, m, _ := ct.Date()
first := time.Date(y, m, 1, 0, 0, 0, 0, loc)
m++
if m > 12 {
y++
}
last := time.Date(y, m, 1, 0, 0, 0, 0, loc).AddDate(0, 0, -1)
return calendarTime{first}, calendarTime{last}
}
func (ct calendarTime) addDay() calendarTime {
return calendarTime{ct.AddDate(0, 0, 1)}
}
func (ct calendarTime) subDay() calendarTime {
return calendarTime{ct.AddDate(0, 0, -1)}
}
func (ct calendarTime) addWeek() calendarTime {
return calendarTime{ct.AddDate(0, 0, 7)}
}
func (ct calendarTime) subWeek() calendarTime {
return calendarTime{ct.AddDate(0, 0, -7)}
}
func (ct calendarTime) addMonth() calendarTime {
return calendarTime{ct.AddDate(0, 1, 0)}
}
func (ct calendarTime) subMonth() calendarTime {
return calendarTime{ct.AddDate(0, -1, 0)}
}
func (ct calendarTime) to(t calendarTime) string {
from := t.Time
to := ct.Time
rel := humanize.RelTime(t.Time, ct.Time, "earlier", "later")
days := int(from.Sub(to).Hours() / 24)
return fmt.Sprintf("%s / %d days", rel, days)
}