Composable view (#394)

* docs: creating nested models

* docs: move nested model to example

* docs: add working nested model example

* refactor: use tea.Batch in nested model example

* refactor: switch to composable view example

* refactor: tab select, add padding to boxes, only focused has border

* fix: add padding to timer to remove UI shift
This commit is contained in:
bashbunni 2022-08-24 14:57:32 -07:00 committed by GitHub
parent 31800cd0a7
commit 30bb43e5ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 158 additions and 0 deletions

View File

@ -0,0 +1,158 @@
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().
Padding(1, 2).
BorderStyle(lipgloss.HiddenBorder())
focusedModelStyle = lipgloss.NewStyle().
Padding(1, 2).
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, 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: change focused model • 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.Start(); err != nil {
log.Fatal(err)
}
}