package fields import ( "bubble/setup/utils" "fmt" "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 msg.String() { case "esc": //Закрываем список m.open = false case "up": //Уменьшаем фокус, поднимаем его m.focus-- if m.focus < 0 { m.focus = len(m.options) - 1 } case "down": //Увеличиваем фокус, опускаем его m.focus++ if m.focus == len(m.options) { m.focus = 0 } case "enter": //Выбираем опцию из списка 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 { //Если список не открыт и был нажат enter, открываем его и устанавливаем фокус на основе опций. switch msg.String() { case "enter": 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) } }