Files

176 lines
3.9 KiB
Go
Raw Permalink Normal View History

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
}