Documentation
¶
Overview ¶
Package modal provides a declarative modal dialog library with automatic hit region management for mouse support.
The library eliminates off-by-one hit region bugs via a render-then-measure pattern and provides automatic keyboard navigation (Tab/Shift+Tab, Enter, Esc) and hover state management.
Quick Start ¶
m := modal.New("Confirm Delete", modal.WithVariant(modal.VariantDanger)).
AddSection(modal.Text("Are you sure you want to delete this item?")).
AddSection(modal.Spacer()).
AddSection(modal.Buttons(
modal.Btn(" Delete ", "delete", modal.BtnDanger()),
modal.Btn(" Cancel ", "cancel"),
))
// In View():
content := m.Render(screenW, screenH, mouseHandler)
// In Update():
if action, cmd := m.HandleKey(keyMsg); action != "" {
switch action {
case "delete":
return performDelete()
case "cancel":
return closeModal()
}
}
Built-in Sections ¶
- Text(s string) - static text, auto-wrapped
- Spacer() - blank line
- Buttons(btns ...ButtonDef) - button row with focus/hover styling
- Checkbox(id, label string, checked *bool) - toggleable checkbox
- Input(id string, model *textinput.Model, opts...) - text input
- Textarea(id string, model *textarea.Model, height int, opts...) - multiline
- List(id string, items []ListItem, selectedIdx *int, opts...) - scrollable list
- When(condition func() bool, section) - conditional rendering
- Custom(renderFn, updateFn) - escape hatch for complex content
Options ¶
- WithWidth(w int) - set modal width (default: 50)
- WithVariant(v Variant) - set visual style (Default, Danger, Warning, Info)
- WithHints(show bool) - show/hide keyboard hints at bottom
- WithPrimaryAction(actionID string) - action for implicit Enter submit
- WithCloseOnBackdropClick(close bool) - close on backdrop click
See the package-level documentation for detailed integration guides.
Index ¶
- Constants
- Variables
- type BtnOption
- type ButtonDef
- type CustomRenderFunc
- type CustomUpdateFunc
- type FocusableInfo
- type InputOption
- type ListItem
- type ListOption
- type Modal
- func (m *Modal) AddSection(s Section) *Modal
- func (m *Modal) FocusedID() string
- func (m *Modal) HandleKey(msg tea.KeyMsg) (action string, cmd tea.Cmd)
- func (m *Modal) HandleMouse(msg tea.MouseMsg, handler *mouse.Handler) string
- func (m *Modal) HoveredID() string
- func (m *Modal) Render(screenW, screenH int, handler *mouse.Handler) string
- func (m *Modal) Reset()
- func (m *Modal) Scroll(delta int)
- func (m *Modal) ScrollOffset() int
- func (m *Modal) SetFocus(id string)
- func (m *Modal) SetScrollOffset(offset int)
- type Option
- type RenderedSection
- type Section
- func Buttons(btns ...ButtonDef) Section
- func Checkbox(id, label string, checked *bool) Section
- func Custom(renderFn CustomRenderFunc, updateFn CustomUpdateFunc) Section
- func Input(id string, model *textinput.Model, opts ...InputOption) Section
- func InputWithLabel(id, label string, model *textinput.Model, opts ...InputOption) Section
- func List(id string, items []ListItem, selectedIdx *int, opts ...ListOption) Section
- func Spacer() Section
- func Text(s string) Section
- func Textarea(id string, model *textarea.Model, height int, opts ...TextareaOption) Section
- func TextareaWithLabel(id, label string, model *textarea.Model, height int, opts ...TextareaOption) Section
- func When(condition func() bool, section Section) Section
- type TextareaOption
- type Variant
Constants ¶
const ( DefaultWidth = 50 MinModalWidth = 30 MaxModalWidth = 120 ModalPadding = 6 // border(2) + horizontal padding(4) )
Default modal dimensions
Variables ¶
var ( // Primary colors Primary = lipgloss.Color("212") // primaryColor Error = lipgloss.Color("196") // errorColor Warning = lipgloss.Color("214") // warningColor Info = lipgloss.Color("45") // cyan Muted = lipgloss.Color("241") // mutedColor BgSecondary = lipgloss.Color("235") // modal background TextMuted = lipgloss.Color("241") // mutedColor BorderNormal = lipgloss.Color("240") // default border )
Colors matching td's monitor/styles.go
var ( Button = lipgloss.NewStyle(). Foreground(lipgloss.Color("252")). Background(lipgloss.Color("238")). Padding(0, 2) ButtonFocused = lipgloss.NewStyle(). Foreground(lipgloss.Color("255")). Background(Primary). Bold(true). Padding(0, 2) ButtonHover = lipgloss.NewStyle(). Foreground(lipgloss.Color("255")). Background(lipgloss.Color("245")). Padding(0, 2) ButtonDanger = lipgloss.NewStyle(). Foreground(lipgloss.Color("252")). Background(lipgloss.Color("238")). Padding(0, 2) ButtonDangerFocused = lipgloss.NewStyle(). Foreground(lipgloss.Color("255")). Background(Error). Bold(true). Padding(0, 2) ButtonDangerHover = lipgloss.NewStyle(). Foreground(lipgloss.Color("255")). Background(lipgloss.Color("203")). Padding(0, 2) )
Button styles matching td's monitor/styles.go
var ( ModalTitle = lipgloss.NewStyle().Bold(true) MutedText = lipgloss.NewStyle().Foreground(Muted) Body = lipgloss.NewStyle() // Plain body text )
Text styles
var ( ListItemNormal = lipgloss.NewStyle(). Foreground(lipgloss.Color("252")) ListItemSelected = lipgloss.NewStyle(). Background(lipgloss.Color("237")). Foreground(lipgloss.Color("255")) ListItemFocused = lipgloss.NewStyle(). Background(lipgloss.Color("237")). Foreground(lipgloss.Color("255")). Bold(true) ListCursor = lipgloss.NewStyle(). Foreground(Primary). Bold(true) )
List styles for list sections
Functions ¶
This section is empty.
Types ¶
type BtnOption ¶
type BtnOption func(*ButtonDef)
BtnOption is a functional option for buttons.
func BtnDanger ¶
func BtnDanger() BtnOption
BtnDanger marks the button as a danger/destructive action.
func BtnPrimary ¶
func BtnPrimary() BtnOption
BtnPrimary is a no-op for compatibility (primary styling is default for focused).
type CustomRenderFunc ¶
type CustomRenderFunc func(contentWidth int, focusID, hoverID string) RenderedSection
CustomRenderFunc is the signature for custom section render functions.
type CustomUpdateFunc ¶
CustomUpdateFunc is the signature for custom section update functions.
type FocusableInfo ¶
type FocusableInfo struct {
ID string // Unique identifier for this element
OffsetX int // X offset relative to section top-left (within content area)
OffsetY int // Y offset relative to section top-left (within content area)
Width int // Width in characters
Height int // Height in lines
}
FocusableInfo describes a focusable element within a section.
type InputOption ¶
type InputOption func(*inputSection)
InputOption is a functional option for Input sections.
func WithSubmitAction ¶
func WithSubmitAction(actionID string) InputOption
WithSubmitAction sets the action ID returned on submit.
func WithSubmitOnEnter ¶
func WithSubmitOnEnter(submit bool) InputOption
WithSubmitOnEnter enables or disables submit-on-enter behavior.
type ListItem ¶
type ListItem struct {
ID string // Unique identifier for this item
Label string // Display text
Data any // Optional associated data
}
ListItem represents an item in a list section.
type ListOption ¶
type ListOption func(*listSection)
ListOption is a functional option for List sections.
func WithMaxVisible ¶
func WithMaxVisible(n int) ListOption
WithMaxVisible sets the maximum number of visible items.
type Modal ¶
type Modal struct {
// contains filtered or unexported fields
}
Modal represents a declarative modal dialog with automatic hit region management.
func (*Modal) AddSection ¶
AddSection adds a section to the modal. Returns the modal for chaining.
func (*Modal) HandleKey ¶
HandleKey processes keyboard input. Returns:
- action: the action ID if triggered ("cancel" for Esc, button/input ID for Enter, etc.)
- cmd: any tea.Cmd from bubbles models (cursor blink, etc.)
func (*Modal) HandleMouse ¶
HandleMouse processes mouse input. Returns the action ID if a clickable element was clicked, empty string otherwise.
func (*Modal) Render ¶
Render renders the modal and registers hit regions. Returns the styled modal content string.
func (*Modal) Scroll ¶
Scroll adjusts the scroll offset by delta lines. Positive delta scrolls down, negative scrolls up. The offset is clamped to valid bounds during Render.
func (*Modal) ScrollOffset ¶
ScrollOffset returns the current scroll offset.
func (*Modal) SetScrollOffset ¶
SetScrollOffset sets the scroll offset to a specific value. The offset is clamped to valid bounds during Render.
type Option ¶
type Option func(*Modal)
Option is a functional option for configuring a Modal.
func WithCloseOnBackdropClick ¶
WithCloseOnBackdropClick controls whether clicking the backdrop dismisses the modal. Defaults to true.
func WithPrimaryAction ¶
WithPrimaryAction sets the action ID returned when input submits implicitly.
type RenderedSection ¶
type RenderedSection struct {
Content string // Rendered string content
Focusables []FocusableInfo // Focusable elements with hit region info
}
RenderedSection is the result of rendering a section.
type Section ¶
type Section interface {
// Render returns the rendered section content and focusable elements.
// contentWidth is the available width for content (modal width minus border/padding).
// focusID is the ID of the currently focused element (for styling).
// hoverID is the ID of the currently hovered element (for styling).
Render(contentWidth int, focusID, hoverID string) RenderedSection
// Update handles input when this section contains the focused element.
// Returns action string if the input triggers an action, plus any tea.Cmd.
Update(msg tea.Msg, focusID string) (action string, cmd tea.Cmd)
}
Section is the interface for modal content sections.
func Custom ¶
func Custom(renderFn CustomRenderFunc, updateFn CustomUpdateFunc) Section
Custom creates a custom section with user-provided render and update functions. If updateFn is nil, updates are no-ops.
func Input ¶
func Input(id string, model *textinput.Model, opts ...InputOption) Section
Input creates an input section wrapping a textinput.Model.
func InputWithLabel ¶
func InputWithLabel(id, label string, model *textinput.Model, opts ...InputOption) Section
InputWithLabel creates an input section with a label.
func List ¶
func List(id string, items []ListItem, selectedIdx *int, opts ...ListOption) Section
List creates a list section with selectable items. selectedIdx is a pointer to the currently selected index (can be nil for no selection).
func TextareaWithLabel ¶
func TextareaWithLabel(id, label string, model *textarea.Model, height int, opts ...TextareaOption) Section
TextareaWithLabel creates a textarea section with a label.
type TextareaOption ¶
type TextareaOption func(*textareaSection)
TextareaOption is a functional option for Textarea sections.