Dropdowns and popovers
Attach a layer to another node with AnchorTo, for autocomplete suggestions, menus, tooltips, and popovers that follow what they belong to.
A dropdown is a layer placed against another node, rather than against the screen.
AnchorTo(id, side) puts a layer against a side
of the node with that ID, aligned with its start.
Show suggestions below an input
Give the input an ID, then anchor a layer to its bottom edge:
type fieldID string
const fieldLanguage fieldID = "language"
func (m *model) View() tea.View {
form := bloom.Column(
bloom.Text("Language"),
bloom.Text(m.language).Style(inputStyle).ID(fieldLanguage),
)
var layers []bloom.LayerNode
if matches := m.suggestions(); len(matches) > 0 {
options := make([]bloom.Child, len(matches))
for i, match := range matches {
options[i] = bloom.Text(match).Style(optionStyle).On(on.Click(func() tea.Cmd {
m.language = match
return nil
}))
}
layers = append(layers, bloom.Layer(bloom.Box(menuStyle, bloom.Column(options...))).
AnchorTo(fieldLanguage, bloom.Bottom))
}
v := m.ui.View(m.width, m.height, bloom.Overlay(form, layers...))
v.MouseMode = tea.MouseModeCellMotion
return v
}m.suggestions() stands for your own matching: it returns the options that match what's been typed.
The dropdown isn't modal, so the rest of the screen still gets clicks. A click on an option runs its handler, like a click on anything else.
Close it on a click elsewhere
A menu that stays open until dismissed can close itself with
on.ClickOutside, which runs when a button goes
down anywhere but on the layer. The click still reaches what it's on:
bloom.Layer(menu).AnchorTo(fieldLanguage, bloom.Bottom).On(on.ClickOutside(m.closeMenu))Choose a side
The side is bloom.Bottom, bloom.Top, bloom.Left, or bloom.Right:
BottomandTopput the layer below or above the node, lined up with its left edge.LeftandRightput it beside the node, lined up with its top edge.
The layer is shifted to stay on screen, so a menu anchored near the right edge moves left rather than being cut
off. Use Offset(x, y) to nudge it, such as one row down to leave a gap.
Follow the node
The anchor is found again every frame, so a popover stays attached when its node moves: when the list scrolls, a
panel resizes, or a slot animates to a new place. Add .ID(...).Animate() to the
layer itself to have it glide from place to place rather than jump.
bloom.Layer(marker).AnchorTo(selectedRow, bloom.Right).ID("marker").Animate()While the anchor node isn't on screen, because it's scrolled away, collapsed, or not in the tree, the layer is hidden too.
Tooltips
A tooltip is the same thing, shown while the pointer is over a node. Following the pointer needs
v.MouseMode = tea.MouseModeAllMotion.
on.Hover runs when the pointer moves onto a node and
off it. Note which button it's over:
bloom.Text("deploy").Style(button).ID(buttonDeploy).On(on.Hover(func(over bool) tea.Cmd {
if over {
m.hovered = buttonDeploy
} else if m.hovered == buttonDeploy {
m.hovered = ""
}
return nil
}))Then anchor a small layer to the hovered button:
if m.hovered != "" {
layers = append(layers, bloom.Layer(bloom.Text(tooltips[m.hovered]).Style(tipStyle)).
AnchorTo(m.hovered, bloom.Top))
}Related
- Dialogs and modals
- Mouse events
- The
formsexample, with an autocomplete dropdown
Last updated on