mirror of
https://github.com/anotherhadi/ilovetui.git
synced 2026-08-21 12:05:49 +02:00
init layout, notifications, tabs & more
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
+101
@@ -0,0 +1,101 @@
|
||||
# modal
|
||||
|
||||
A centered popup box on top of a dimmed background, triggered from anywhere in a bubbletea
|
||||
program via an exported `tea.Msg` (`ShowMsg`/`Show`) rather than a direct reference to the
|
||||
`Model` that ends up rendering it - standard Elm architecture, no IPC between processes.
|
||||
|
||||
It composites over an already-rendered string, so it has no dependency on
|
||||
`github.com/anotherhadi/ilovetui/layout`: the same `Model` works whether the host uses `layout`
|
||||
for its main content or not.
|
||||
|
||||
## Quick start
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/anotherhadi/ilovetui/modal"
|
||||
)
|
||||
|
||||
type model struct {
|
||||
m modal.Model
|
||||
width, height int
|
||||
}
|
||||
|
||||
func newModel() model {
|
||||
return model{m: modal.New()}
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd { return m.m.Init() }
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyPressMsg:
|
||||
if msg.String() == "d" {
|
||||
return m, modal.Show("Delete file?", "This can't be undone.\n\ny: confirm esc: cancel")
|
||||
}
|
||||
if msg.String() == "esc" && m.m.Open() {
|
||||
return m, modal.Close()
|
||||
}
|
||||
}
|
||||
|
||||
var cmd tea.Cmd
|
||||
m.m, cmd = m.m.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m model) View() tea.View {
|
||||
background := renderYourUI(m.width, m.height)
|
||||
view := tea.NewView(m.m.Render(background))
|
||||
view.AltScreen = true
|
||||
return view
|
||||
}
|
||||
```
|
||||
|
||||
Any component in the same bubbletea program can trigger a modal via `modal.Show`, without holding
|
||||
a reference to the `modal.Model` that will actually render it - that `Model` just needs to see
|
||||
every `tea.Msg` the program produces (i.e. get its `Update` called from the top-level `Update`),
|
||||
same as any other child model.
|
||||
|
||||
## Showing and dismissing
|
||||
|
||||
```go
|
||||
return m, modal.Show("Delete file?", "This can't be undone.", modal.WithID("confirm"))
|
||||
|
||||
return m, modal.Dismiss("confirm") // close a specific modal by id
|
||||
return m, modal.Close() // close whichever modal is on top, whatever its id
|
||||
```
|
||||
|
||||
- `modal.Open()` reports whether at least one modal is currently shown - handy for a host that
|
||||
wants to route key presses to the modal (e.g. `esc` to dismiss, `enter` to confirm) instead of
|
||||
its normal UI while one is open.
|
||||
- `modal.TopID()` returns the id of the topmost (currently interactive) modal, or `""` if none is
|
||||
open - useful to tell which modal a generic key like `enter` should act on.
|
||||
- A modal shown without `WithID` gets an auto-generated id that's never returned to the caller -
|
||||
only a modal shown with `WithID` can be targeted by `Dismiss` later. Showing again with the same
|
||||
id replaces it in place instead of stacking a duplicate.
|
||||
|
||||
## Stacking
|
||||
|
||||
Modals stack: showing a second one while the first is still open pushes it on top, dimming both
|
||||
the background and the first modal to the same flat color. Dismissing the top one reveals the one
|
||||
beneath, still in full color. This is what lets a "delete?" confirmation open a nested "really
|
||||
sure?" modal without any special-casing.
|
||||
|
||||
## Styling
|
||||
|
||||
```go
|
||||
m := modal.New(modal.WithMaxWidth(60), modal.WithMaxHeight(20), modal.WithStyles(myStyles))
|
||||
|
||||
return m, modal.Show("Title", "Message", modal.WithModalStyle(oneOffStyles))
|
||||
```
|
||||
|
||||
`WithMaxWidth`/`WithMaxHeight` cap how large a modal box can grow before wrapping/truncating; a
|
||||
modal narrower than the cap shrinks to fit its content instead of padding out to it. A modal can
|
||||
also never overflow past the edge of whatever background it's rendered on, regardless of these
|
||||
caps. `WithStyles` sets the default look for every modal shown by this `Model`; `WithModalStyle`
|
||||
(a `Show` option) overrides it for one modal alone. `DefaultStyles()` builds from `style.S`: the
|
||||
box borrows `PanelFocused`'s border (the modal is what has focus while it's open), the dim color
|
||||
reuses `Subtle`.
|
||||
|
||||
## Examples
|
||||
|
||||
- `examples/modal` - open/dismiss, nested modals, styled from theme colors.
|
||||
Reference in New Issue
Block a user