bubbletea/examples/composable-views/main.go

163 lines
3.7 KiB
Go
Raw Permalink Normal View History

package main
import (
"fmt"
"log"
"time"
"github.com/charmbracelet/bubbles/spinner"
"github.com/charmbracelet/bubbles/timer"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
/*
This example assumes an existing understanding of commands and messages. If you
haven't already read our tutorials on the basics of Bubble Tea and working
with commands, we recommend reading those first.
Find them at:
https://github.com/charmbracelet/bubbletea/tree/master/tutorials/commands
https://github.com/charmbracelet/bubbletea/tree/master/tutorials/basics
*/
// sessionState is used to track which model is focused
type sessionState uint
const (
defaultTime = time.Minute
timerView sessionState = iota
spinnerView
)
var (
// Available spinners
spinners = []spinner.Spinner{
spinner.Line,
spinner.Dot,
spinner.MiniDot,
spinner.Jump,
spinner.Pulse,
spinner.Points,
spinner.Globe,
spinner.Moon,
spinner.Monkey,
}
modelStyle = lipgloss.NewStyle().
Width(15).
Height(5).
Align(lipgloss.Center, lipgloss.Center).
BorderStyle(lipgloss.HiddenBorder())
focusedModelStyle = lipgloss.NewStyle().
Width(15).
Height(5).
Align(lipgloss.Center, lipgloss.Center).
BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("69"))
spinnerStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("69"))
helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241"))
)
type mainModel struct {
state sessionState
timer timer.Model
spinner spinner.Model
index int
}
func newModel(timeout time.Duration) mainModel {
m := mainModel{state: timerView}
m.timer = timer.New(timeout)
m.spinner = spinner.New()
return m
}
func (m mainModel) Init() tea.Cmd {
// start the timer and spinner on program start
return tea.Batch(m.timer.Init(), m.spinner.Tick)
}
func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
var cmds []tea.Cmd
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
case "tab":
if m.state == timerView {
m.state = spinnerView
} else {
m.state = timerView
}
case "n":
if m.state == timerView {
m.timer = timer.New(defaultTime)
cmds = append(cmds, m.timer.Init())
} else {
m.Next()
m.resetSpinner()
cmds = append(cmds, m.spinner.Tick)
}
}
switch m.state {
// update whichever model is focused
case spinnerView:
m.spinner, cmd = m.spinner.Update(msg)
cmds = append(cmds, cmd)
default:
m.timer, cmd = m.timer.Update(msg)
cmds = append(cmds, cmd)
}
case spinner.TickMsg:
m.spinner, cmd = m.spinner.Update(msg)
cmds = append(cmds, cmd)
case timer.TickMsg:
m.timer, cmd = m.timer.Update(msg)
cmds = append(cmds, cmd)
}
return m, tea.Batch(cmds...)
}
func (m mainModel) View() string {
var s string
model := m.currentFocusedModel()
if m.state == timerView {
s += lipgloss.JoinHorizontal(lipgloss.Top, focusedModelStyle.Render(fmt.Sprintf("%4s", m.timer.View())), modelStyle.Render(m.spinner.View()))
} else {
s += lipgloss.JoinHorizontal(lipgloss.Top, modelStyle.Render(fmt.Sprintf("%4s", m.timer.View())), focusedModelStyle.Render(m.spinner.View()))
}
s += helpStyle.Render(fmt.Sprintf("\ntab: focus next • n: new %s • q: exit\n", model))
return s
}
func (m mainModel) currentFocusedModel() string {
if m.state == timerView {
return "timer"
}
return "spinner"
}
func (m *mainModel) Next() {
if m.index == len(spinners)-1 {
m.index = 0
} else {
m.index++
}
}
func (m *mainModel) resetSpinner() {
m.spinner = spinner.New()
m.spinner.Style = spinnerStyle
m.spinner.Spinner = spinners[m.index]
}
func main() {
p := tea.NewProgram(newModel(defaultTime))
if _, err := p.Run(); err != nil {
log.Fatal(err)
}
}