1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
package main
import (
"fmt"
tui "github.com/grindlemire/go-tui"
)
type fileList struct {
files []string
selected *tui.State[int]
scrollY *tui.State[int]
hideScrollbar *tui.State[bool]
content *tui.Ref
}
func FileList() *fileList {
files := []string{
"main.go", "app.go", "app_loop.go", "app_render.go",
"buffer.go", "cell.go", "color.go", "component.go",
"dirty.go", "element.go", "element_options.go",
"element_render.go", "element_scroll.go", "escape.go",
"event.go", "focus.go", "key.go", "keymap.go",
"layout.go", "mount.go", "ref.go", "render.go",
"state.go", "style.go", "terminal.go", "watcher.go",
}
return &fileList{
files: files,
selected: tui.NewState(0),
scrollY: tui.NewState(0),
hideScrollbar: tui.NewState(false),
content: tui.NewRef(),
}
}
func (f *fileList) scrollBy(delta int) {
el := f.content.El()
if el == nil {
return
}
_, maxY := el.MaxScroll()
newY := f.scrollY.Get() + delta
if newY < 0 {
newY = 0
}
if newY > maxY {
newY = maxY
}
f.scrollY.Set(newY)
}
func (f *fileList) moveTo(idx int) {
if idx < 0 {
idx = len(f.files) - 1
}
if idx >= len(f.files) {
idx = 0
}
f.selected.Set(idx)
// Keep selected item visible by adjusting scroll
el := f.content.El()
if el == nil {
return
}
_, vpH := el.ViewportSize()
y := f.scrollY.Get()
if idx < y {
f.scrollY.Set(idx)
} else if idx >= y+vpH {
f.scrollY.Set(idx - vpH + 1)
}
}
func (f *fileList) KeyMap() tui.KeyMap {
return tui.KeyMap{
tui.On(tui.KeyEscape, func(ke tui.KeyEvent) { ke.App().Stop() }),
tui.On(tui.Rune('j'), func(ke tui.KeyEvent) { f.moveTo(f.selected.Get() + 1) }),
tui.On(tui.Rune('k'), func(ke tui.KeyEvent) { f.moveTo(f.selected.Get() - 1) }),
tui.On(tui.KeyDown, func(ke tui.KeyEvent) { f.moveTo(f.selected.Get() + 1) }),
tui.On(tui.KeyUp, func(ke tui.KeyEvent) { f.moveTo(f.selected.Get() - 1) }),
tui.On(tui.KeyPageDown, func(ke tui.KeyEvent) { f.moveTo(f.selected.Get() + 10) }),
tui.On(tui.KeyPageUp, func(ke tui.KeyEvent) { f.moveTo(f.selected.Get() - 10) }),
tui.On(tui.KeyHome, func(ke tui.KeyEvent) { f.moveTo(0) }),
tui.On(tui.KeyEnd, func(ke tui.KeyEvent) { f.moveTo(len(f.files) - 1) }),
tui.On(tui.Rune('h'), func(ke tui.KeyEvent) { f.hideScrollbar.Set(!f.hideScrollbar.Get()) }),
}
}
func (f *fileList) HandleMouse(me tui.MouseEvent) bool {
switch me.Button {
case tui.MouseWheelUp:
f.scrollBy(-3)
return true
case tui.MouseWheelDown:
f.scrollBy(3)
return true
}
return false
}
templ (f *fileList) Render() {
<div class="flex-col p-1 border-rounded border-cyan">
<span class="text-gradient-cyan-magenta font-bold">Files</span>
<div
ref={f.content}
class="overflow-y-scroll scrollbar-cyan scrollbar-thumb-bright-cyan"
height={12}
hideScrollbar={f.hideScrollbar.Get()}
scrollOffset={0, f.scrollY.Get()}>
for i, name := range f.files {
if i == f.selected.Get() {
<span class="text-cyan font-bold bg-bright-black">{fmt.Sprintf(" > %s ", name)}</span>
} else {
<span>{fmt.Sprintf(" %s", name)}</span>
}
}
</div>
<div class="flex justify-between">
<span class="font-dim">j/k navigate | h toggle scrollbar | esc quit</span>
<span class="font-dim">{fmt.Sprintf("%d/%d", f.selected.Get()+1, len(f.files))}</span>
</div>
</div>
}
|