Initial Commit
This commit is contained in:
commit
87a522daab
22 changed files with 1555 additions and 0 deletions
34
tui/model.go
Normal file
34
tui/model.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package tui
|
||||
|
||||
import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
type viewMode int
|
||||
|
||||
const (
|
||||
monthView viewMode = iota
|
||||
hourlyView
|
||||
formView
|
||||
)
|
||||
|
||||
|
||||
type model struct {
|
||||
cursorRow int // which row the cursor is sitting on used for cursor highlight
|
||||
cursorCol int // which col the cursor is sitiing on used for cursor highlight
|
||||
monthIndex int // what month 1-12 that is being displayed
|
||||
year int // the current year being displayed
|
||||
startOffset int // weekday offset where day 1 starts
|
||||
daysInMonth int // a count of how many days there are in the month to account for 30,31,or 28
|
||||
|
||||
mode viewMode
|
||||
form *huh.Form
|
||||
// For hourly view
|
||||
selectedDay int // which day we are looking at in hourly view
|
||||
hourCursor int // which hour (0-23) is selected in hourly view
|
||||
}
|
||||
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return nil
|
||||
}
|
||||
51
tui/newEventForm.go
Normal file
51
tui/newEventForm.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package tui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"git.skdevstudios.com/SK-Development-Studios/go-cal-tui/internal/storage"
|
||||
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
)
|
||||
|
||||
func showEventForm(ctx context.Context, onSubmit func(storage.EventInput)) error {
|
||||
var title, description, year, month, day, startHour, endHour, color string
|
||||
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().Title("Title").Value(&title),
|
||||
huh.NewInput().Title("Description").Value(&description),
|
||||
huh.NewInput().Title("Year").Value(&year),
|
||||
huh.NewInput().Title("Month").Value(&month),
|
||||
huh.NewInput().Title("Day").Value(&day),
|
||||
huh.NewInput().Title("Start Hour (0–23)").Value(&startHour),
|
||||
huh.NewInput().Title("End Hour (0–23)").Value(&endHour),
|
||||
huh.NewInput().Title("Color (e.g. 99 or #ff5733)").Value(&color),
|
||||
),
|
||||
)
|
||||
|
||||
if err := form.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
yr, _ := strconv.Atoi(year)
|
||||
mo, _ := strconv.Atoi(month)
|
||||
da, _ := strconv.Atoi(day)
|
||||
sh, _ := strconv.Atoi(startHour)
|
||||
eh, _ := strconv.Atoi(endHour)
|
||||
|
||||
onSubmit(storage.EventInput{
|
||||
Title: title,
|
||||
Description: description,
|
||||
Year: int32(yr),
|
||||
Month: int32(mo),
|
||||
Day: int32(da),
|
||||
StartHour: int32(sh),
|
||||
EndHour: int32(eh),
|
||||
Color: color,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
46
tui/run.go
Normal file
46
tui/run.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package tui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.skdevstudios.com/SK-Development-Studios/go-cal-tui/internal/storage"
|
||||
"git.skdevstudios.com/SK-Development-Studios/go-cal-tui/internal/logger"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
//Table definition constants
|
||||
const (
|
||||
numRows = 6
|
||||
numCols = 7
|
||||
cellW = 10
|
||||
cellH = 1
|
||||
)
|
||||
func Run() error {
|
||||
if err := storage.InitDB("data"); err != nil {
|
||||
logOut := fmt.Errorf("Failed to initalize BadgerDB: %w",err)
|
||||
logger.Log.Fatal(logOut)
|
||||
}
|
||||
defer storage.CloseDB()
|
||||
|
||||
// Get the current datetime
|
||||
now := time.Now()
|
||||
|
||||
// Set the start date used for opening the current month instead of 1/1/1970
|
||||
start := time.Date(now.Year(), now.Month(), 1,0,0,0,0, time.UTC)
|
||||
|
||||
// This gets the weekday of the first of the month so that we have the correct day with the correct date
|
||||
offset := int(start.Weekday())
|
||||
|
||||
p := tea.NewProgram(model{
|
||||
year: now.Year(),
|
||||
monthIndex: int(now.Month()) - 1,
|
||||
cursorRow: offset / numCols,
|
||||
cursorCol: offset % numCols,
|
||||
startOffset: offset,
|
||||
mode: monthView,
|
||||
})
|
||||
_, err := p.Run()
|
||||
return err
|
||||
}
|
||||
28
tui/styles.go
Normal file
28
tui/styles.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package tui
|
||||
|
||||
import "github.com/charmbracelet/lipgloss"
|
||||
|
||||
// Styles for calendar cells and headers
|
||||
var (
|
||||
// this is the style thats used as the default style for the month view
|
||||
cellStyle = lipgloss.NewStyle().Width(cellW).Height(cellH).Align(lipgloss.Center)
|
||||
// this is the style that sets the current selected hours bg to orange and text to white
|
||||
selectedStyle = cellStyle.Copy().Bold(true).Background(lipgloss.Color("12")).Foreground(lipgloss.Color("15"))
|
||||
// this is the style that sets the color of cells that are not in use for a given month
|
||||
unselectedStyle = cellStyle.Copy().Background(lipgloss.Color("236")).Foreground(lipgloss.Color("250"))
|
||||
// this is the style that sets the color of todays cell to purple bg white text
|
||||
todayStyle = cellStyle.Copy().Background(lipgloss.Color("99")).Foreground(lipgloss.Color("15")).Bold(true)
|
||||
// this is the style that defines the header where the current months name goes
|
||||
headerStyle = lipgloss.NewStyle().Width(numCols * cellW).Align(lipgloss.Center).Bold(true)
|
||||
// this is the style that defines the DoW header where mon-sun go
|
||||
daysOfWeekStyle = lipgloss.NewStyle().Width(cellW).Align(lipgloss.Center).Bold(true)
|
||||
|
||||
// Styles for hourly view cells
|
||||
// this is the style thats used as the default for the hours view
|
||||
hourCellStyle = lipgloss.NewStyle().Width(cellW * 3).Height(cellH).Align(lipgloss.Left).PaddingLeft(1)
|
||||
// This is the style that sets the current selected hours style to orange bg white text
|
||||
hourSelectedStyle = hourCellStyle.Copy().Bold(true).Background(lipgloss.Color("12")).Foreground(lipgloss.Color("15"))
|
||||
// this is the style that sets the current hour to purple bg and white text
|
||||
currentHourStyle = hourCellStyle.Copy().Background(lipgloss.Color("99")).Foreground(lipgloss.Color("15")).Bold(true)
|
||||
)
|
||||
|
||||
109
tui/update.go
Normal file
109
tui/update.go
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
package tui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.skdevstudios.com/SK-Development-Studios/go-cal-tui/internal/storage"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch m.mode {
|
||||
case monthView:
|
||||
return m.updateMonthView(msg)
|
||||
case hourlyView:
|
||||
return m.updateHourlyView(msg)
|
||||
default:
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m model) updateMonthView(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
firstDay := time.Date(m.year, time.Month(m.monthIndex+1), 1, 0, 0, 0, 0, time.UTC)
|
||||
m.startOffset = int(firstDay.Weekday())
|
||||
m.daysInMonth = time.Date(m.year, time.Month(m.monthIndex+2), 0, 0, 0, 0, 0, time.UTC).Day()
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "ctrl+c", "q":
|
||||
return m, tea.Quit
|
||||
case "a": // Previous month
|
||||
if m.monthIndex == 0 {
|
||||
m.monthIndex = 11
|
||||
m.year--
|
||||
} else {
|
||||
m.monthIndex--
|
||||
}
|
||||
case "d": // Next month
|
||||
if m.monthIndex == 11 {
|
||||
m.monthIndex = 0
|
||||
m.year++
|
||||
} else {
|
||||
m.monthIndex++
|
||||
}
|
||||
case "up":
|
||||
if m.cursorRow > 0 {
|
||||
m.cursorRow--
|
||||
}
|
||||
case "down":
|
||||
if m.cursorRow < numRows-1 {
|
||||
m.cursorRow++
|
||||
}
|
||||
case "left":
|
||||
if m.cursorCol > 0 {
|
||||
m.cursorCol--
|
||||
}
|
||||
case "right":
|
||||
if m.cursorCol < numCols-1 {
|
||||
m.cursorCol++
|
||||
}
|
||||
case "enter":
|
||||
dayIndex := m.cursorRow*numCols + m.cursorCol
|
||||
if dayIndex >= m.startOffset && dayIndex < m.startOffset+m.daysInMonth {
|
||||
m.selectedDay = dayIndex - m.startOffset + 1
|
||||
m.hourCursor = 0
|
||||
m.mode = hourlyView
|
||||
}
|
||||
}
|
||||
|
||||
// Clamp cursor to valid days
|
||||
dayIndex := m.cursorRow*numCols + m.cursorCol
|
||||
if dayIndex < m.startOffset || dayIndex >= m.startOffset+m.daysInMonth {
|
||||
m.cursorRow = m.startOffset / numCols
|
||||
m.cursorCol = m.startOffset % numCols
|
||||
}
|
||||
return m, nil
|
||||
default:
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m model) updateHourlyView(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "n":
|
||||
showEventForm(context.Background(), func(e storage.EventInput) {
|
||||
_ = storage.SaveEvent(e)
|
||||
})
|
||||
case "esc":
|
||||
m.mode = monthView
|
||||
return m, nil
|
||||
case "ctrl+c", "q":
|
||||
return m, tea.Quit
|
||||
case "up":
|
||||
if m.hourCursor > 0 {
|
||||
m.hourCursor--
|
||||
}
|
||||
case "down":
|
||||
if m.hourCursor < 23 {
|
||||
m.hourCursor++
|
||||
}
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
0
tui/view.go
Normal file
0
tui/view.go
Normal file
Loading…
Add table
Add a link
Reference in a new issue