GoSetup-bubble/setup/fields/select.go

160 lines
4.4 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package fields
import (
"bubble/setup/utils"
"bubble/types"
"fmt"
"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
)
// SelectModel структура описывающая модель поля выбора опции
type SelectModel struct {
label string //Имя поля
description string //Описание пол
options []string //Элементы выбора
focus int //Фокус для управления опциями
open bool //Открыт ли список
chosen string //Выбранный элемент
optionsCount int //Количество элементов к показу
validate textinput.ValidateFunc
err error
}
// NewSelectModel создаем новую поля выбора опции
func NewSelectModel(label, value, description string, options []string, validator textinput.ValidateFunc) *SelectModel {
//Проверка, что указанный по умолчанию элемент есть в списке опции
selectModel := &SelectModel{
options: options,
label: label,
description: description,
focus: len(options),
validate: validator,
optionsCount: 10,
}
found := false
for i := range options {
if options[i] == value {
found = true
}
}
if !found {
value = ""
}
selectModel.chosen = value
selectModel.SelectValidate()
return selectModel
}
func (m *SelectModel) Init() tea.Cmd {
return textinput.Blink
}
func (m *SelectModel) Update(msg tea.Msg) (FieldModel, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.KeyMsg:
//Отслеживаем нажатия клавиш.
//Если список открыт, то отслеживаем клавиши отдельно
if m.open {
switch {
case key.Matches(msg, types.MainKeys.Close):
//Обработчик клавиши закрытия списка опций
m.open = false
case key.Matches(msg, types.MainKeys.Up):
//Обработчик клавиши перехода на прошлую опцию
m.focus--
if m.focus < 0 {
m.focus = len(m.options) - 1
}
case key.Matches(msg, types.MainKeys.Down):
//Обработчик клавиши перехода на следующую опцию
m.focus++
if m.focus == len(m.options) {
m.focus = 0
}
case key.Matches(msg, types.MainKeys.Confirm):
//Обработчик клавиши выбора опции. Если список пуст, ничего не делаем.
if len(m.options) == 0 || m.focus >= len(m.options) {
return m, nil
}
m.chosen = m.options[m.focus]
m.open = false
m.focus = 0
m.SelectValidate()
return m, nil
}
} else {
//Если список не открыт и была нажата клавиша подтверждения, открываем его и рисуем опции списка.
//Иначе просто передаем сообщение нажатия клавиш в input.
switch {
case key.Matches(msg, types.MainKeys.Confirm):
m.open = true
for optionIdx := range m.options {
if m.options[optionIdx] == m.chosen {
m.focus = optionIdx
}
}
if m.chosen == "" && len(m.options) > 0 {
m.focus = 0
}
}
}
}
return m, cmd
}
func (m *SelectModel) View() string {
res := fmt.Sprintf("%s: %s", m.label, m.chosen)
if m.err != nil && !m.open {
res += fmt.Sprintf("\nОшибка валидации: %s", m.err)
} else if m.description != "" {
res += fmt.Sprintf("\n%s", m.description)
}
if m.open && len(m.options) > 0 {
start, end := utils.CalculateSelectOptionsIndex(m.focus, m.optionsCount, len(m.options))
for i := start; i < end; i++ {
option := m.options[i]
prefix := "\n\t"
if m.focus == i {
prefix += "-> "
} else {
prefix += " "
}
res += prefix + option
}
}
return res
}
func (m *SelectModel) Close() {
m.open = false
}
func (m *SelectModel) Focus() {
}
func (m *SelectModel) Blur() {
m.open = false
}
func (m *SelectModel) CaptureKeys() bool {
return m.open
}
func (m *SelectModel) GetLabel() string {
return m.label
}
func (m *SelectModel) GetValue() interface{} {
return m.chosen
}
func (m *SelectModel) SelectValidate() {
if m.validate != nil {
m.err = m.validate(m.chosen)
}
}