176 lines
3.9 KiB
Go
176 lines
3.9 KiB
Go
package model
|
||
|
||
import (
|
||
"fmt"
|
||
|
||
"github.com/StevanFreeborn/term-tier/internal/handlers"
|
||
"github.com/StevanFreeborn/term-tier/internal/state"
|
||
"github.com/StevanFreeborn/term-tier/internal/styles"
|
||
"github.com/charmbracelet/bubbles/textinput"
|
||
tea "github.com/charmbracelet/bubbletea"
|
||
"github.com/charmbracelet/lipgloss"
|
||
)
|
||
|
||
type Mode int
|
||
|
||
const (
|
||
ModeNav Mode = iota
|
||
ModeInput
|
||
)
|
||
|
||
type Model struct {
|
||
State state.State
|
||
KeyMsgHandlers []handlers.KeyMsgHandler
|
||
WinSizeMsgHandlers []handlers.WinSizeMsgHandler
|
||
}
|
||
|
||
type option func(m *Model)
|
||
|
||
func New(state state.State, opts ...option) Model {
|
||
ti := textinput.New()
|
||
ti.CharLimit = 50
|
||
ti.Width = 30
|
||
|
||
m := Model{
|
||
State: state,
|
||
}
|
||
|
||
for _, opt := range opts {
|
||
opt(&m)
|
||
}
|
||
|
||
return m
|
||
}
|
||
|
||
func WithKeyMsgHandlers(handlers []handlers.KeyMsgHandler) option {
|
||
return func(m *Model) {
|
||
m.KeyMsgHandlers = handlers
|
||
}
|
||
}
|
||
|
||
func WithWinSizeMsgHandlers(handlers []handlers.WinSizeMsgHandler) option {
|
||
return func(m *Model) {
|
||
m.WinSizeMsgHandlers = handlers
|
||
}
|
||
}
|
||
|
||
func (m Model) Init() tea.Cmd {
|
||
return textinput.Blink
|
||
}
|
||
|
||
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||
switch msg := msg.(type) {
|
||
case tea.WindowSizeMsg:
|
||
for _, handler := range m.WinSizeMsgHandlers {
|
||
if handler.Match(m.State, msg) {
|
||
return m, handler.Handle(m.State, msg)
|
||
}
|
||
}
|
||
case tea.KeyMsg:
|
||
for _, handler := range m.KeyMsgHandlers {
|
||
if handler.Match(m.State, msg) {
|
||
return m, handler.Handle(m.State, msg)
|
||
}
|
||
}
|
||
}
|
||
|
||
return m, nil
|
||
}
|
||
|
||
func (m Model) View() string {
|
||
if m.State.Mode() == state.InputMode {
|
||
return fmt.Sprintf(
|
||
"\n %s\n\n%s\n\n (Enter to Confirm, Esc to Cancel)",
|
||
m.State.InputTitle(),
|
||
styles.InputBoxStyle.Render(m.State.Input().View()),
|
||
)
|
||
}
|
||
|
||
if m.State.Mode() == state.HelpMode {
|
||
helpText := " HELP MENU\n\n"
|
||
|
||
for _, handler := range m.KeyMsgHandlers {
|
||
if desc := handler.Description(); desc != "" {
|
||
keys := handler.KeysHandled()
|
||
helpText += fmt.Sprintf(" %s: %s\n", keys, desc)
|
||
}
|
||
}
|
||
|
||
helpText += "\n (Press ? to close this menu)"
|
||
|
||
box := styles.HelpBoxStyle.Render(helpText)
|
||
|
||
return lipgloss.Place(
|
||
m.State.WindowWidth(),
|
||
m.State.WindowHeight(),
|
||
lipgloss.Center,
|
||
lipgloss.Center,
|
||
box,
|
||
)
|
||
}
|
||
|
||
s := "[?] Help ️ [q] Quit\n\n"
|
||
|
||
availableWidth := max(m.State.WindowWidth()-15, 20)
|
||
|
||
for tierIndex, tier := range m.State.Tiers() {
|
||
var rows []string
|
||
var currentRowItems []string
|
||
currentWidth := 0
|
||
|
||
for itemIndex, item := range tier.Items {
|
||
renderStyle := styles.ItemStyle
|
||
isFocused := tierIndex == m.State.CursorRow() && itemIndex == m.State.CursorCol()
|
||
|
||
if isFocused {
|
||
if m.State.Dragging() {
|
||
renderStyle = styles.DraggingItemStyle
|
||
} else {
|
||
renderStyle = styles.SelectedItemStyle
|
||
}
|
||
}
|
||
|
||
renderedItem := renderStyle.Render(string(item))
|
||
itemWidth := lipgloss.Width(renderedItem)
|
||
|
||
if currentWidth+itemWidth > availableWidth && len(currentRowItems) > 0 {
|
||
rows = append(rows, lipgloss.JoinHorizontal(lipgloss.Top, currentRowItems...))
|
||
currentRowItems = []string{renderedItem}
|
||
currentWidth = itemWidth
|
||
} else {
|
||
currentRowItems = append(currentRowItems, renderedItem)
|
||
currentWidth += itemWidth
|
||
}
|
||
}
|
||
|
||
if len(tier.Items) == 0 && tierIndex == m.State.CursorRow() {
|
||
ghost := lipgloss.NewStyle().Foreground(lipgloss.Color("240")).Render(" [ ]")
|
||
currentRowItems = append(currentRowItems, ghost)
|
||
}
|
||
|
||
if len(currentRowItems) > 0 {
|
||
rows = append(rows, lipgloss.JoinHorizontal(lipgloss.Top, currentRowItems...))
|
||
}
|
||
|
||
itemBlock := lipgloss.JoinVertical(lipgloss.Left, rows...)
|
||
|
||
blockHeight := lipgloss.Height(itemBlock)
|
||
|
||
label := styles.TierLabelStyle.
|
||
Background(tier.Color).
|
||
Height(blockHeight).
|
||
AlignVertical(lipgloss.Center).
|
||
Render(tier.Name)
|
||
|
||
row := lipgloss.JoinHorizontal(lipgloss.Center, label, itemBlock)
|
||
|
||
s += row + "\n\n"
|
||
}
|
||
|
||
if m.State.StatusMsg() != "" {
|
||
s += styles.StatusStyle.Render(" STATUS: " + m.State.StatusMsg())
|
||
}
|
||
|
||
return s
|
||
}
|