charmbracelet / charmbracelet/bubbles
List Width not respected
- Dominant language
- Go
- Stars
- 8.9k
- Forks
- 457
- Avg merge
- 1d 18h
- Merged PRs (30d)
- 5
Description
**Describe the bug**
When creating a new list, width is a required argument. This value does absolutely nothing unless it is set to a value smaller than the width of the widest list item. Looking through the code that implements list, I could not find anywhere in which the width member of the `list.Model` struct is used to force the rendering to the specified width. It does appear to be used in some capacity when rendering the filter input and the help section, but that's it. Even then, it does not appear to have an effect from my testing.
**Setup**
Please complete the following information along with version numbers, if applicable.
- OS: KDE Neon
- Shell: zsh
- Terminal Emulator: Kitty
- Terminal Multiplexer: N/A
- Locale: US/UTF-8
**To Reproduce**
Steps to reproduce the behavior:
1. Use `list.New` to create a new list. Set the width to a value larger than the width of your widest list item.
2. render the list in your view function.
3. Observe that list does not respect tha value of the `width` argument.
**Source Code**
This is the source code from following the tutorial in this [video](https://www.youtube.com/watch?v=hF7qMEowPTw):
```go
package main
import (
"log"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
type status int
const layoutDivisor int = 4
const (
todo status = iota
inProgress
done
)
/* Styling */
var (
columnStyle = lipgloss.NewStyle().
Padding(2, 1).
Border(lipgloss.HiddenBorder())
focusedStyle = lipgloss.NewStyle().
Padding(2, 1).
Border(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("62"))
helpStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("41"))
)
/* Task List Item */
type Task struct {
title string
description string
status status
}
func (t *Task) Title() string { return t.title }
func (t *Task) Description() string { return t.description }
func (t *Task) FilterValue() string { return t.title }
/* Main Model */
type Model struct {
focused status
lists []list.Model
err error
loaded bool
}
func NewModel() *Model {
m := new(Model)
return m
}
func (m *Model) onNextList() {
if m.focused == done {
m.focused = todo
} else {
m.focused++
}
}
func (m *Model) onPrevList() {
if m.focused == todo {
m.focused = done
} else {
m.focused--
}
}
func (m *Model) initLists(width, height int) {
_, heightOffset := columnStyle.GetFrameSize()
// listWidth := (width - (widthOffset * 3)) / 3
listHeight := height - heightOffset
defaultList := list.New([]list.Item{}, list.NewDefaultDelegate(), 10, listHeight)
defaultList.SetShowHelp(false)
m.lists = []list.Model{defaultList, defaultList, defaultList}
m.lists[todo].Title = "Todo"
m.lists[todo].SetItems([]list.Item{
&Task{status: todo, title: "Buy Milk", description: "Strawberry Milk"},
&Task{status: todo, title: "Eat Sushi", description: "California Roll"},
&Task{status: todo, title: "Do Laundry", description: "Or have wrinkles"},
})
m.lists[inProgress].Title = "In Progress"
m.lists[inProgress].SetItems([]list.Item{
&Task{status: todo, title: "Be Cool", description: "You know it baby!"},
})
m.lists[done].Title = "Done"
m.lists[done].SetItems([]list.Item{
&Task{status: todo, title: "VetRec Interview 2", description: "Went well"},
&Task{status: todo, title: "Learn Go", description: "Language Learned"},
})
m.loaded = true
}
func (m Model) Init() tea.Cmd {
return nil
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.initLists(msg.Width, msg.Height)
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c":
return m, tea.Quit
case "left":
m.onPrevList()
case "right":
m.onNextList()
case "tab":
m.onNextList()
}
}
m.lists[m.focused], cmd = m.lists[m.focused].Update(msg)
return m, cmd
}
func (m Model) View() string {
if !m.loaded {
return "Loading..."
}
todoList := m.lists[todo].View()
inProgressList := m.lists[inProgress].View()
doneList := m.lists[done].View()
switch m.focused {
case todo:
todoList = focusedStyle.Render(todoList)
inProgressList = columnStyle.Render(inProgressList)
doneList = columnStyle.Render(doneList)
case inProgress:
todoList = columnStyle.Render(todoList)
inProgressList = focusedStyle.Render(inProgressList)
doneList = columnStyle.Render(doneList)
case done:
todoList = columnStyle.Render(todoList)
inProgressList = columnStyle.Render(inProgressList)
doneList = focusedStyle.Render(doneList)
}
return lipgloss.JoinHorizontal(
lipgloss.Center,
todoList,
inProgressList,
doneList,
)
}
func main() {
model := NewModel()
p := tea.NewProgram(model, tea.WithAltScreen())
_, err := p.Run()
if err != nil {
log.Fatal(err)
}
}
```
**Expected behavior**
The value passed to width when creating a new list via `list.New` should set the width of the rendered list component.
**Screenshots**
**Additional context**
Add any other context about the problem here.
Contributor guide
Research direction
Start at the list.New and list.Model rendering path in the Bubbles list component, using the supplied reproduction to inspect how the width argument is applied. Verify the behavior with an item set wider and narrower than the requested width; done means the rendered list consistently uses the configured width without breaking its existing content, filter, or help rendering.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 42/100