Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,45 @@ type ConfirmPopoverYesMsg struct{}
// ConfirmPopoverNoMsg is returned when the user cancels the popover.
type ConfirmPopoverNoMsg struct{}

// --- Filter popover messages ---

// FilterSection represents a group of checkable options in the filter popover.
type FilterSection struct {
Title string
Options []FilterOption
}

// FilterOption is a single checkable item in a filter section.
type FilterOption struct {
Label string
Value string
Selected bool
}

// FilterInput represents a text input field in the filter popover.
type FilterInput struct {
Title string
Placeholder string
Value string
}

// OpenFilterPopoverMsg tells the shell to open the filter popover.
type OpenFilterPopoverMsg struct {
Sections []FilterSection
Inputs []FilterInput
}

// CloseFilterPopoverMsg tells the shell to close the filter popover without applying.
type CloseFilterPopoverMsg struct{}

// ApplyFilterPopoverMsg is returned when the user applies the filter selections.
// Selections maps section title to the list of selected option values.
// Inputs maps input title to the entered value.
type ApplyFilterPopoverMsg struct {
Selections map[string][]string
Inputs map[string]string
}

// SelectionProvider is implemented by panels that can provide a selected item label.
type SelectionProvider interface {
SelectedLabel() string
Expand Down
114 changes: 114 additions & 0 deletions popover/examples/filter/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package main

import (
"fmt"
"os"
"strings"

tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/felipeospina21/tuishell"
"github.com/felipeospina21/tuishell/popover"
"github.com/felipeospina21/tuishell/style"
)

type model struct {
theme style.Theme
filter popover.FilterModel
open bool
result string
width int
height int
}

func newModel() model {
t := defaultTheme()
return model{theme: t, filter: popover.NewFilter(t)}
}

func (m model) Init() tea.Cmd { return nil }

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.width, m.height = msg.Width, msg.Height
case tea.KeyPressMsg:
if !m.open {
switch msg.String() {
case "f":
m.open = true
m.filter.Open(
[]tuishell.FilterSection{
{Title: "Status", Options: []tuishell.FilterOption{
{Label: "In Progress", Value: "in_progress", Selected: true},
{Label: "To Do", Value: "todo"},
{Label: "In Review", Value: "in_review"},
}},
{Title: "Priority", Options: []tuishell.FilterOption{
{Label: "Critical", Value: "critical"},
{Label: "High", Value: "high", Selected: true},
{Label: "Medium", Value: "medium"},
{Label: "Low", Value: "low"},
}},
{Title: "Type", Options: []tuishell.FilterOption{
{Label: "Bug", Value: "bug", Selected: true},
{Label: "Story", Value: "story"},
{Label: "Task", Value: "task"},
}},
},
[]tuishell.FilterInput{
{Title: "Sprint", Placeholder: "e.g. 42"},
},
)
return m, nil
case "q", "ctrl+c":
return m, tea.Quit
}
return m, nil
}
case tuishell.CloseFilterPopoverMsg:
m.open = false
return m, nil
case tuishell.ApplyFilterPopoverMsg:
m.open = false
var parts []string
for title, vals := range msg.Selections {
parts = append(parts, fmt.Sprintf("%s: %s", title, strings.Join(vals, ", ")))
}
for title, val := range msg.Inputs {
if val != "" {
parts = append(parts, fmt.Sprintf("%s: %s", title, val))
}
}
m.result = strings.Join(parts, " | ")
return m, nil
}

if m.open {
var cmd tea.Cmd
m.filter, cmd = m.filter.Update(msg)
return m, cmd
}
return m, nil
}

func (m model) View() tea.View {
bg := fmt.Sprintf("\n Press 'f' to open filter popover, 'q' to quit\n\n Applied: %s\n", m.result)
bg = lipgloss.NewStyle().Width(m.width).Height(m.height).Render(bg)

screen := bg
if m.open {
screen = m.filter.View(bg, m.width, m.height)
}

v := tea.NewView(screen)
v.AltScreen = true
return v
}

func main() {
if _, err := tea.NewProgram(newModel()).Run(); err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}
}
43 changes: 43 additions & 0 deletions popover/examples/filter/theme.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"charm.land/lipgloss/v2"
"github.com/felipeospina21/tuishell/style"
)

func defaultTheme() style.Theme {
return style.Theme{
Primary: lipgloss.Color("#b8a6ff"),
PrimaryBright: lipgloss.Color("#9673ff"),
PrimaryFg: lipgloss.Color("#f2f0ff"),
PrimaryDim: lipgloss.Color("#4c01d6"),

Info: lipgloss.Color("#3ac4d9"),
InfoBright: lipgloss.Color("#1ca7be"),
Success: lipgloss.Color("#6beaaf"),
SuccessBright: lipgloss.Color("#3ad994"),
Danger: lipgloss.Color("#f9a8a8"),
DangerBright: lipgloss.Color("#f47575"),
Warning: lipgloss.Color("#ffe043"),
WarningBright: lipgloss.Color("#ffcc14"),
Caution: lipgloss.Color("#ff8237"),

Text: lipgloss.Color("#C4C4C4"),
TextInverse: lipgloss.Color("#111"),
TextDimmed: lipgloss.Color("#777777"),
Muted: lipgloss.Color("#999999"),
Dim: lipgloss.Color("#444444"),
Border: lipgloss.Color("#3f4145"),
ModalBorder: lipgloss.Color("#666666"),
SurfaceDim: lipgloss.Color("#1e1e24"),
SelectionBorder: lipgloss.Color("#AD58B4"),

StatusText: lipgloss.Color("#FFFDF5"),
StatusNormal: lipgloss.Color("#6914ff"),
StatusLoading: lipgloss.Color("#1A7A94"),
StatusError: lipgloss.Color("#CE3060"),
StatusDemo: lipgloss.Color("#4E8212"),
StatusAccent1: lipgloss.Color("#A550DF"),
StatusAccent2: lipgloss.Color("#6124DF"),
}
}
Loading
Loading