add filter

Signed-off-by: Hadi <[email protected]>
This commit is contained in:
Hadi
2026-08-13 18:22:44 +02:00
parent fc35732dbf
commit 0f7738be68
7 changed files with 117 additions and 11 deletions
+76 -6
View File
@@ -2,6 +2,7 @@ package input
import (
"fmt"
"strings"
"charm.land/bubbles/v2/key"
tea "charm.land/bubbletea/v2"
@@ -39,11 +40,41 @@ func (m *Model) handleSetLayout(msg setLayoutMsg) tea.Cmd {
return fetchLayoutCmd()
}
// visibleLayouts returns the layouts matching the current filter query (by
// code or name, case-insensitive), or every layout when there's no query.
func (m Model) visibleLayouts() []inputpkg.Layout {
q := strings.ToLower(strings.TrimSpace(m.filterInput.Value()))
if q == "" {
return inputpkg.Layouts
}
matches := make([]inputpkg.Layout, 0, len(inputpkg.Layouts))
for _, l := range inputpkg.Layouts {
if strings.Contains(strings.ToLower(l.Name), q) || strings.Contains(strings.ToLower(l.Code), q) {
matches = append(matches, l)
}
}
return matches
}
// clampCursor keeps the cursor within the currently visible (filtered)
// list, called whenever the filter query — and so the list length —
// changes.
func (m *Model) clampCursor() {
n := len(m.visibleLayouts())
switch {
case m.cursor >= n:
m.cursor = n - 1
case m.cursor < 0:
m.cursor = 0
}
}
func (m Model) selectedLayout() *inputpkg.Layout {
if m.cursor < 0 || m.cursor >= len(inputpkg.Layouts) {
list := m.visibleLayouts()
if m.cursor < 0 || m.cursor >= len(list) {
return nil
}
return &inputpkg.Layouts[m.cursor]
return &list[m.cursor]
}
func (m *Model) updateLayout(msg tea.Msg) tea.Cmd {
@@ -54,13 +85,36 @@ func (m *Model) updateLayout(msg tea.Msg) tea.Cmd {
g := keys.Keys.Global
in := keys.Keys.Input
if m.filtering {
switch {
case key.Matches(keyMsg, g.Escape):
m.filtering = false
m.filterInput.Blur()
m.filterInput.SetValue("")
m.cursor = 0
case key.Matches(keyMsg, in.Select):
m.filtering = false
m.filterInput.Blur()
m.clampCursor()
default:
var cmd tea.Cmd
m.filterInput, cmd = m.filterInput.Update(keyMsg)
m.clampCursor()
return cmd
}
return nil
}
switch {
case key.Matches(keyMsg, in.Filter):
m.filtering = true
m.filterInput.Focus()
case key.Matches(keyMsg, g.Up):
if m.cursor > 0 {
m.cursor--
}
case key.Matches(keyMsg, g.Down):
if m.cursor < len(inputpkg.Layouts)-1 {
if m.cursor < len(m.visibleLayouts())-1 {
m.cursor++
}
case key.Matches(keyMsg, in.Select):
@@ -90,16 +144,32 @@ func (m Model) renderLayout(width, height int) string {
}
current := subtleStyle().Render("Current: ") + inputpkg.LayoutName(m.currentLayout) + subtleStyle().Render(" ("+m.currentLayout+")")
lines := []string{title, "", current, ""}
lines := []string{title, "", current}
switch {
case m.filtering:
lines = append(lines, m.filterInput.View())
case m.filterInput.Value() != "":
lines = append(lines, subtleStyle().Render(fmt.Sprintf("Filter: %q — press / to change", m.filterInput.Value())))
}
lines = append(lines, "")
list := m.visibleLayouts()
available := height - len(lines) - 2
if available < 1 {
available = 1
}
p, start, end := paginate(m.cursor, len(inputpkg.Layouts), available)
if len(list) == 0 {
lines = append(lines, ilovetui.S.Faint.Render("No layout matches the filter."))
return lipgloss.JoinVertical(lipgloss.Left, lines...)
}
p, start, end := paginate(m.cursor, len(list), available)
for i := start; i < end; i++ {
lines = append(lines, m.renderLayoutRow(inputpkg.Layouts[i], i == m.cursor))
lines = append(lines, m.renderLayoutRow(list[i], i == m.cursor))
}
footer := renderPaginatorDots(p)