modal

package
v0.22.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 26, 2026 License: MIT Imports: 7 Imported by: 0

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

View Source
const (
	DefaultWidth  = 50
	MinModalWidth = 30
	MaxModalWidth = 120
	ModalPadding  = 6 // border(2) + horizontal padding(4)
)

Default modal dimensions

Variables

View Source
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

View Source
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

View Source
var (
	ModalTitle = lipgloss.NewStyle().Bold(true)
	MutedText  = lipgloss.NewStyle().Foreground(Muted)
	Body       = lipgloss.NewStyle() // Plain body text
)

Text styles

View Source
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 ButtonDef

type ButtonDef struct {
	Label    string
	ID       string
	IsDanger bool
}

ButtonDef defines a button in a button row.

func Btn

func Btn(label, id string, opts ...BtnOption) ButtonDef

Btn creates a button definition.

type CustomRenderFunc

type CustomRenderFunc func(contentWidth int, focusID, hoverID string) RenderedSection

CustomRenderFunc is the signature for custom section render functions.

type CustomUpdateFunc

type CustomUpdateFunc func(msg tea.Msg, focusID string) (action string, cmd tea.Cmd)

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 struct {
	// contains filtered or unexported fields
}

Modal represents a declarative modal dialog with automatic hit region management.

func New

func New(title string, opts ...Option) *Modal

New creates a new Modal with the given title and options.

func (*Modal) AddSection

func (m *Modal) AddSection(s Section) *Modal

AddSection adds a section to the modal. Returns the modal for chaining.

func (*Modal) FocusedID

func (m *Modal) FocusedID() string

FocusedID returns the currently focused element ID.

func (*Modal) HandleKey

func (m *Modal) HandleKey(msg tea.KeyMsg) (action string, cmd tea.Cmd)

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

func (m *Modal) HandleMouse(msg tea.MouseMsg, handler *mouse.Handler) string

HandleMouse processes mouse input. Returns the action ID if a clickable element was clicked, empty string otherwise.

func (*Modal) HoveredID

func (m *Modal) HoveredID() string

HoveredID returns the currently hovered element ID.

func (*Modal) Render

func (m *Modal) Render(screenW, screenH int, handler *mouse.Handler) string

Render renders the modal and registers hit regions. Returns the styled modal content string.

func (*Modal) Reset

func (m *Modal) Reset()

Reset resets the modal state (focus, hover, scroll).

func (*Modal) Scroll

func (m *Modal) Scroll(delta int)

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

func (m *Modal) ScrollOffset() int

ScrollOffset returns the current scroll offset.

func (*Modal) SetFocus

func (m *Modal) SetFocus(id string)

SetFocus sets focus to a specific element by ID.

func (*Modal) SetScrollOffset

func (m *Modal) SetScrollOffset(offset int)

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

func WithCloseOnBackdropClick(close bool) Option

WithCloseOnBackdropClick controls whether clicking the backdrop dismisses the modal. Defaults to true.

func WithHints

func WithHints(show bool) Option

WithHints enables the keyboard hint line at the bottom.

func WithPrimaryAction

func WithPrimaryAction(actionID string) Option

WithPrimaryAction sets the action ID returned when input submits implicitly.

func WithVariant

func WithVariant(v Variant) Option

WithVariant sets the modal visual variant.

func WithWidth

func WithWidth(w int) Option

WithWidth sets the modal width.

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 Buttons

func Buttons(btns ...ButtonDef) Section

Buttons creates a button row section.

func Checkbox

func Checkbox(id, label string, checked *bool) Section

Checkbox creates a checkbox section.

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 Spacer

func Spacer() Section

Spacer creates a blank line section.

func Text

func Text(s string) Section

Text creates a static text section.

func Textarea

func Textarea(id string, model *textarea.Model, height int, opts ...TextareaOption) Section

Textarea creates a textarea section wrapping a textarea.Model.

func TextareaWithLabel

func TextareaWithLabel(id, label string, model *textarea.Model, height int, opts ...TextareaOption) Section

TextareaWithLabel creates a textarea section with a label.

func When

func When(condition func() bool, section Section) Section

When creates a conditional section that only renders when condition() returns true.

type TextareaOption

type TextareaOption func(*textareaSection)

TextareaOption is a functional option for Textarea sections.

type Variant

type Variant int

Variant represents the visual style of the modal.

const (
	VariantDefault Variant = iota // Primary border color
	VariantDanger                 // Red border, danger button styles
	VariantWarning                // Yellow/amber border
	VariantInfo                   // Blue border
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL