Skip to content

Commit

Permalink
Add doc comments; clean up styles
Browse files Browse the repository at this point in the history
  • Loading branch information
Kevin Miller committed Feb 23, 2024
1 parent f2300fc commit 8ab3a69
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 9 deletions.
3 changes: 3 additions & 0 deletions breadcrumb/model.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Package Breadcrumb is a component that consumes a pointer ot a navstack.Model
// and renders a breadcrumb trail. It is used to give the user a sense of where
// they are in the application.
package breadcrumb

import (
Expand Down
4 changes: 4 additions & 0 deletions bubbleo.go
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
// Bubbleo is a collection of [BubbleTea] components for building robust terminal user interfaces.
// Initially we are supporting hierarchical menus, via a navigation stack and supporting components such as
// breadcrumbs, menus, and a composit component called shell which puts them all together.
// [BubbleTea]: https://github.com/charmbracelet/bubbletea
package bubbleo
36 changes: 32 additions & 4 deletions menu/model.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
// Package Menu takes a list of choices allowing the user to select a component
// to push onto the navigation stack. Each choice has a title and a description and
// a component model implementing [tea.Model].
// [tea.Model] https://github.com/charmbracelet/bubbletea/blob/a256e76ff5ff142d747ad833c7aa784113f8558c/tea.go#L39
package menu

import (
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/kevm/bubbleo/navstack"
"github.com/kevm/bubbleo/styles"
"github.com/kevm/bubbleo/utils"
Expand All @@ -24,16 +29,31 @@ func (i choiceItem) Title() string { return i.title }
func (i choiceItem) Description() string { return i.desc }
func (i choiceItem) FilterValue() string { return i.title + i.desc }

// MenuStyles is a struct that holds the styles for the menu
// This mostly a passthrough for bubble/list component styles.
type MenuStyles struct {
ListTitleStyle lipgloss.Style
ListItemStyles list.DefaultItemStyles
}

type Model struct {
Choices []Choice
list list.Model

list list.Model
selected *Choice
delegate list.DefaultDelegate
}

// New setups up a new menu model
func New(title string, choices []Choice, selected *Choice) Model {

styles := MenuStyles{
ListTitleStyle: styles.ListTitleStyle,
ListItemStyles: list.NewDefaultItemStyles(),
}

delegation := list.NewDefaultDelegate()
delegation.Styles = styles.ListItemStyles
items := make([]list.Item, len(choices))
selectedIndex := -1
for i, choice := range choices {
Expand All @@ -47,6 +67,7 @@ func New(title string, choices []Choice, selected *Choice) Model {
Choices: choices,
list: list.New(items, delegation, 120, 20),
selected: selected,
delegate: delegation,
}

if selected != nil {
Expand All @@ -62,9 +83,6 @@ func New(title string, choices []Choice, selected *Choice) Model {
model.list.SetShowStatusBar(false)
model.list.SetShowHelp(false)

//TODO: figure out height long term.
// model.list.SetSize(window.Width, window.Height-window.TopOffset)

chooseKeyBinding := key.NewBinding(
key.WithKeys("enter"),
key.WithHelp("enter", "choose"),
Expand All @@ -83,6 +101,13 @@ func (m Model) Init() tea.Cmd {
return nil
}

// SetStyles allows you to customize the styles used by the menu. This is mostly a passthrough
// to the bubble/list component used by the menu.
func (m Model) SetStyles(s MenuStyles) {
m.list.Styles.Title = s.ListTitleStyle
m.delegate.Styles = s.ListItemStyles
}

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

switch msg := msg.(type) {
Expand Down Expand Up @@ -110,14 +135,17 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, cmd
}

// SelectedChoice returns the currently selected menu choice
func (m Model) SelectedChoice() *Choice {
return m.selected
}

// SetSize sets the size of the menu
func (m *Model) SetSize(w tea.WindowSizeMsg) {
m.list.SetSize(w.Width, w.Height)
}

// View renders the menu. When no choices are present, nothing is rendered.
func (m Model) View() string {
// display menu if choices are present.
if len(m.Choices) > 0 {
Expand Down
24 changes: 24 additions & 0 deletions navstack/model.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
// Package Navstack manages a stack of NavigationItems which can be pushed or popped from the stack.
// The top most stack navigation item is used by [BubbleTea] to Update and renders it's View.
// When pushing and popping items from the stack, the new view to be presented is sent a tea.WindowSizeMsg
// to ensure it's view can be presented correctly. When the last item is popped from the stack the application will quit.
// NavigationItem models which implement the Closable interface will have their Close method called when they are popped from the stack.
// This is useful for cleaning up resources that may not be garbage collected when a view a no longer needed.
// [BubbleTea]: https://github.com/charmbracelet/bubbletea
package navstack

import (
tea "github.com/charmbracelet/bubbletea"
"github.com/kevm/bubbleo/window"
)

// Closable is an interface for models that have resources that need to be cleaned up when
// they are no longer needed. The navigation stack checks for this interface when popping items.
type Closable interface {
Close() error
}
Expand All @@ -14,6 +23,8 @@ type Model struct {
window *window.Model
}

// New creates a new navigation stack model. The window is used to
// constrain the view within the container of the navigation stack.
func New(w *window.Model) Model {
model := Model{
stack: []NavigationItem{},
Expand All @@ -32,6 +43,10 @@ func (m Model) Init() tea.Cmd {
return top.Init()
}

// Phsh pushes a new navigation item onto the stack.
// The new navigation item is given a tea.WindowSizeMsg to ensure it's view can be presented correctly.
// The item's Init method is called and resulting command is processed by [BubbleTea].
// This new item will be the top most item on the stack and thus will be rendered.
func (m *Model) Push(item NavigationItem) tea.Cmd {

wmsg := m.window.GetWindowSizeMsg()
Expand All @@ -42,6 +57,10 @@ func (m *Model) Push(item NavigationItem) tea.Cmd {
return tea.Batch(cmd, item.Init())
}

// Pop removes the top most navigation item from the stack.
// If the item implements the Closable interface the Close method is called.
// The new top most item on the stack is given a tea.WindowSizeMsg to ensure it's view can be presented correctly.
// If there are no more items on the stack the application will quit.
func (m *Model) Pop() tea.Cmd {
top := m.Top()
if top == nil {
Expand All @@ -66,6 +85,7 @@ func (m *Model) Pop() tea.Cmd {
return tea.Batch(cmds...)
}

// Top returns the top most navigation item on the stack.
func (m Model) Top() *NavigationItem {
if len(m.stack) == 0 {
return nil
Expand All @@ -75,6 +95,8 @@ func (m Model) Top() *NavigationItem {
return &top
}

// StackSummary returns a list of titles for each item on the stack.
// This is currently used by the breadcrumb component to render the breadcrumb trail.
func (m Model) StackSummary() []string {
summary := []string{}
for _, item := range m.stack {
Expand All @@ -84,6 +106,7 @@ func (m Model) StackSummary() []string {
return summary
}

// Update processes messages for the top most navigation item on the stack.
func (m *Model) Update(msg tea.Msg) tea.Cmd {
top := m.Top()
switch msg := msg.(type) {
Expand Down Expand Up @@ -119,6 +142,7 @@ func (m *Model) Update(msg tea.Msg) tea.Cmd {
}
}

// View renders the top most navigation item on the stack.
func (m Model) View() string {

top := m.Top()
Expand Down
5 changes: 5 additions & 0 deletions navstack/navitem.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@ package navstack

import tea "github.com/charmbracelet/bubbletea"

// NavigationItem is a component that represents an item in the navigation stack.
// The top most item on the stack is rendered.
type NavigationItem struct {
Title string
Model tea.Model
}

// Init is called when the item is pushed onto the stack.
func (n NavigationItem) Init() tea.Cmd {
return n.Model.Init()
}

// Update receives messages when the item is on top of the stack.
func (n NavigationItem) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
nm, cmd := n.Model.Update(msg)
n.Model = nm
return n, cmd
}

// View is calledn when the item is on top of the stack.
func (n NavigationItem) View() string {
return n.Model.View()
}
7 changes: 7 additions & 0 deletions shell/model.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Package Shell is a basic wrapper around the navstack and breadcrumb packages
// It provides a basic navigation mechanism while showing breadcrumb view of where the user is
// within the navigation stack.
package shell

import (
Expand All @@ -16,6 +19,7 @@ type Model struct {
window *window.Model
}

// New creates a new shell model
func New() Model {
w := window.New(120, 30, 0, 0)
ns := navstack.New(&w)
Expand All @@ -29,6 +33,7 @@ func New() Model {
}
}

// Init determines the size of the widow used by the navigation stack.
func (m Model) Init() tea.Cmd {

w, h := m.Breadcrumb.FrameStyle.GetFrameSize()
Expand All @@ -38,11 +43,13 @@ func (m Model) Init() tea.Cmd {
return utils.Cmdize(m.window.GetWindowSizeMsg())
}

// Update passes messages to the navigation stack.
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
cmd := m.Navstack.Update(msg)
return m, cmd
}

// View renders the breadcrumb and the navigation stack.
func (m Model) View() string {
bc := m.Breadcrumb.View()
nav := m.Navstack.View()
Expand Down
6 changes: 1 addition & 5 deletions styles/styles.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ var (
foregroundColor = lipgloss.AdaptiveColor{Light: "#343433", Dark: "#C1C6B2"}
backgroundColor = lipgloss.AdaptiveColor{Light: "#D9DCCF", Dark: "#353533"}

AppStyle = lipgloss.NewStyle().Padding(2, 5)
ListStyle = lipgloss.NewStyle().Margin(1, 2)
ListItemStyle = lipgloss.NewStyle().PaddingLeft(4)
ListTitleStyle = lipgloss.NewStyle().MarginLeft(2).Background(backgroundColor).Foreground(foregroundColor).Bold(true)

ListTitleStyle = lipgloss.NewStyle().MarginLeft(2).Background(backgroundColor).Foreground(foregroundColor).Bold(true)
BreadCrumbFrameStyle = lipgloss.NewStyle().Background(backgroundColor).Foreground(foregroundColor).Margin(1)
)

0 comments on commit 8ab3a69

Please sign in to comment.