todo-with-db/goto-do/cmd/root.go

128 lines
2.7 KiB
Go
Raw Permalink Normal View History

2024-10-26 17:12:01 -04:00
/*
2024-10-26 17:13:06 -04:00
Copyright © 2024 tiff github@tifflabs.org
2024-10-26 17:12:01 -04:00
*/
package cmd
import (
2025-03-20 22:04:20 -04:00
"fmt"
2025-05-01 13:02:23 -04:00
"os"
"os/signal"
2025-03-20 22:04:20 -04:00
2025-05-01 13:02:23 -04:00
"github.com/charmbracelet/bubbles/table"
2025-03-20 22:04:20 -04:00
tea "github.com/charmbracelet/bubbletea"
2025-05-01 13:02:23 -04:00
"github.com/charmbracelet/lipgloss"
"context"
"database/sql"
"flag"
"log"
"time"
2024-10-26 17:12:01 -04:00
)
2025-05-01 13:02:23 -04:00
var baseStyle = lipglodd.NewStyle().
BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("240"))
// Think of this `struct` as a JavaScript object or Python dictionary
2025-03-20 22:04:20 -04:00
type model struct {
2025-05-01 13:02:23 -04:00
// methods? Probably not
2025-03-20 22:04:20 -04:00
todos []string // items on the to-do list
2025-05-01 13:02:23 -04:00
cursor int // which to-do list item our cursor is pointing at
// so it looks like Go doesn't have `_self` or `this`?
2025-03-20 22:04:20 -04:00
selected map[int]struct{} // which to-do items are selected
2025-05-01 13:02:23 -04:00
table table.Model
2025-03-20 22:04:20 -04:00
}
2024-10-26 17:12:01 -04:00
2025-05-01 13:02:23 -04:00
func (m model) Init() tea.Cmd { return nil }
2025-03-20 22:04:20 -04:00
func initialModel() model {
return model{
// Our to-do list is a grocery list
todos: []string{""},
2024-10-26 17:12:01 -04:00
2025-03-20 22:04:20 -04:00
// A map which indicates which choices are selected. We're using
// the map like a mathematical set. The keys refer to the indexes
// of the `choices` slice, above.
selected: make(map[int]struct{}),
}
2024-10-26 17:12:01 -04:00
}
2025-03-20 22:04:20 -04:00
func (m model) Init() tea.Cmd {
// Just return `nil`, which means "no I/O right now, please."
return nil
}
2024-10-26 17:47:40 -04:00
2025-03-20 22:04:20 -04:00
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
// Is it a key press?
case tea.KeyMsg:
2024-10-26 17:47:40 -04:00
2025-03-20 22:04:20 -04:00
// Cool, what was the actual key pressed?
switch msg.String() {
2024-10-26 17:47:40 -04:00
2025-03-20 22:04:20 -04:00
// These keys should exit the program.
case "ctrl+c", "q":
return m, tea.Quit
// The "up" and "k" keys move the cursor up
case "up", "k":
if m.cursor > 0 {
m.cursor--
}
2024-10-26 17:47:40 -04:00
2025-03-20 22:04:20 -04:00
// The "down" and "j" keys move the cursor down
case "down", "j":
if m.cursor < len(m.todos)-1 {
m.cursor++
}
// The "enter" key and the spacebar (a literal space) toggle
// the selected state for the item that the cursor is pointing at.
case "enter", " ":
_, ok := m.selected[m.cursor]
if ok {
delete(m.selected, m.cursor)
} else {
m.selected[m.cursor] = struct{}{}
}
}
2024-10-26 17:12:01 -04:00
}
2025-03-20 22:04:20 -04:00
// Return the updated model to the Bubble Tea runtime for processing.
// Note that we're not returning a command.
return m, nil
2024-10-26 17:12:01 -04:00
}
2025-03-20 22:04:20 -04:00
func (m model) View() string {
// The header
s := "What do you want to do today?\n\n"
2024-10-26 17:12:01 -04:00
2025-03-20 22:04:20 -04:00
// Iterate over our choices
for i, todo := range m.todos {
2024-10-26 17:12:01 -04:00
2025-03-20 22:04:20 -04:00
// Is the cursor pointing at this choice?
cursor := " " // no cursor
if m.cursor == i {
cursor = ">" // cursor!
}
2024-10-26 17:12:01 -04:00
2025-03-20 22:04:20 -04:00
// Is this choice selected?
checked := " " // not selected
if _, ok := m.selected[i]; ok {
checked = "x" // selected!
}
2024-10-26 17:12:01 -04:00
2025-03-20 22:04:20 -04:00
// Render the row
s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, todo)
}
// The footer
s += "\nPress q to quit.\n"
// Send the UI for rendering
return s
}