mirror of
https://github.com/anotherhadi/ilovetui.git
synced 2026-08-21 12:05:49 +02:00
aa6496901f
Signed-off-by: Hadi <hadi@example.fr>
110 lines
3.9 KiB
Go
110 lines
3.9 KiB
Go
package drawer
|
|
|
|
import tea "charm.land/bubbletea/v2"
|
|
|
|
// Side is which edge of the background a Drawer is anchored to.
|
|
type Side int
|
|
|
|
const (
|
|
// Left anchors the drawer to the left edge (the zero value, so a Drawer
|
|
// built without WithSide opens on the left).
|
|
Left Side = iota
|
|
// Right anchors the drawer to the right edge.
|
|
Right
|
|
)
|
|
|
|
// Drawer is one sidebar panel on the stack.
|
|
type Drawer struct {
|
|
Title string
|
|
// Content is the drawer's body: a full model, updated and rendered by
|
|
// the drawer.Model while it's on top of the stack. Wrap a plain string
|
|
// with Text for a drawer with nothing to interact with.
|
|
Content tea.Model
|
|
Side Side
|
|
// Width, if non-zero, fixes the drawer's total width (border and
|
|
// padding included, same unit as the Model's WithMaxWidth) instead of
|
|
// shrinking to fit what Content draws, and Title. Still capped by whatever actually
|
|
// fits the background.
|
|
Width int
|
|
// Style, if non-nil, overrides the Model's default Styles for this
|
|
// drawer alone.
|
|
Style *Styles
|
|
}
|
|
|
|
// DrawerOption configures a Drawer built by Show.
|
|
type DrawerOption func(*Drawer)
|
|
|
|
// WithSide anchors the drawer to the given Side (default Left).
|
|
func WithSide(s Side) DrawerOption {
|
|
return func(d *Drawer) { d.Side = s }
|
|
}
|
|
|
|
// WithWidth fixes the drawer's total width instead of shrinking to fit its
|
|
// content, still capped by whatever actually fits the background.
|
|
func WithWidth(w int) DrawerOption {
|
|
return func(d *Drawer) { d.Width = w }
|
|
}
|
|
|
|
// WithDrawerStyle overrides the Model's default Styles for this drawer
|
|
// alone, for a one-off custom look instead of the shared theme.
|
|
func WithDrawerStyle(s Styles) DrawerOption {
|
|
return func(d *Drawer) { d.Style = &s }
|
|
}
|
|
|
|
func newDrawer(title string, content tea.Model, opts ...DrawerOption) Drawer {
|
|
d := Drawer{Title: title, Content: content}
|
|
for _, opt := range opts {
|
|
opt(&d)
|
|
}
|
|
return d
|
|
}
|
|
|
|
// ShowMsg tells a drawer.Model to display Drawer, pushing it on top of the
|
|
// stack. Any component in the same bubbletea program can trigger one via
|
|
// Show, without holding a reference to the drawer.Model that will actually
|
|
// render it - that Model just needs to see every tea.Msg the program
|
|
// produces, same as any other child model.
|
|
type ShowMsg struct{ Drawer Drawer }
|
|
|
|
// Show returns a tea.Cmd that opens a new drawer on top of the stack,
|
|
// anchored to the left edge by default. content is a model, so a drawer can
|
|
// hold anything a pane can - a file list, a filter form, a picker that
|
|
// reports its choice with its own tea.Msg:
|
|
//
|
|
// return m, drawer.Show("Files", newFileList(dir), drawer.WithSide(drawer.Right))
|
|
// return m, drawer.Show("Nav", drawer.Text("Home\nProjects"))
|
|
//
|
|
// The content's Init runs when the drawer opens, and it receives every
|
|
// message while it's the topmost drawer (see Model.Update).
|
|
func Show(title string, content tea.Model, opts ...DrawerOption) tea.Cmd {
|
|
d := newDrawer(title, content, opts...)
|
|
return func() tea.Msg { return ShowMsg{Drawer: d} }
|
|
}
|
|
|
|
// DismissMsg closes the topmost drawer. The stack is a plain LIFO: a drawer
|
|
// is closed by being on top, never by being named.
|
|
type DismissMsg struct{}
|
|
|
|
// Close returns a tea.Cmd that closes the topmost drawer - which is also the
|
|
// only one that can act (see Model.Update), so a content model closes itself
|
|
// by returning it:
|
|
//
|
|
// return c, drawer.Close()
|
|
func Close() tea.Cmd {
|
|
return func() tea.Msg { return DismissMsg{} }
|
|
}
|
|
|
|
// text is a model wrapping a fixed string: a drawer body with nothing to
|
|
// update.
|
|
type text string
|
|
|
|
func (t text) Init() tea.Cmd { return nil }
|
|
func (t text) Update(tea.Msg) (tea.Model, tea.Cmd) { return t, nil }
|
|
func (t text) View() tea.View { return tea.NewView(string(t)) }
|
|
|
|
// Text wraps a plain string as drawer content, for the common drawer that has
|
|
// nothing to interact with:
|
|
//
|
|
// drawer.Show("Nav", drawer.Text("Home\nProjects\nSettings"))
|
|
func Text(s string) tea.Model { return text(s) }
|