package setup import ( "bubble/setup/ui" "bubble/types" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) type ModalModel struct { modalType types.ModalType title string message string buttons []types.ModalButton focus int width int height int onResult func(result any) tea.Cmd } func NewModalModel(msg types.ShowModalMsg, width, height int) *ModalModel { if len(msg.ModalButtons) == 0 { msg.ModalButtons = []types.ModalButton{{Text: "Ok", Value: true}} } return &ModalModel{ modalType: msg.Type, title: msg.Title, message: msg.Message, buttons: msg.ModalButtons, focus: 0, width: width, height: height, onResult: msg.OnResult, } } func (m *ModalModel) Init() tea.Cmd { return nil } func (m *ModalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: m.width = msg.Width m.height = msg.Height return m, nil case tea.KeyMsg: switch msg.String() { case "left", "h", "shift+tab": m.focus-- if m.focus < 0 { m.focus = len(m.buttons) - 1 } case "right", "l", "tab": m.focus++ if m.focus >= len(m.buttons) { m.focus = 0 } case "enter", " ": //вытаскиваем значение кнопки result := m.buttons[m.focus].Value var cmd tea.Cmd if m.onResult != nil { //если функция кнопки не пустая, мы передаем в функцию значение кнопки cmd = m.onResult(result) } return m, tea.Batch( //объединяем 2 действия, функцию кнопки и закрытие модального окна cmd, func() tea.Msg { return types.CloseModalMsg{} }, ) } } return m, nil } func (m *ModalModel) View() string { containerWidth, containerHeight, _, _ := ui.ContentSizes(m.width, m.height) modalWidth := min(containerWidth, ui.ModalMaxWidth) modalHeight := min(containerHeight, ui.ModalMaxHeight) message := lipgloss.NewStyle().Render(m.message) var buttons []string for i, button := range m.buttons { style := ui.ModalButtonNormal if i == m.focus { style = ui.ModalButtonFocused } buttons = append(buttons, style.Render(button.Text)) } buttonRow := lipgloss.JoinHorizontal(lipgloss.Center, buttons...) modalBody := lipgloss.JoinVertical(lipgloss.Center, message, "", buttonRow) var borderColor lipgloss.Color var modalTitleStyle lipgloss.Style switch m.modalType { case types.ModalError: borderColor = ui.ColorError modalTitleStyle = ui.ModalTitleError case types.ModalWarning, types.ModalConfirm: borderColor = ui.ColorWarning modalTitleStyle = ui.ModalTitleWarning default: borderColor = ui.ColorAccent modalTitleStyle = ui.ModalTitleDefault } // //switch m.modalType { //case types.ModalError: // modalStyle = ui.ModalBorderError // modalTitleStyle = ui.ModalTitleError //case types.ModalWarning, types.ModalConfirm: // modalStyle = ui.ModalBorderWarning // modalTitleStyle = ui.ModalTitleWarning //default: // modalStyle = ui.ModalBorderInfo // modalTitleStyle = ui.ModalTitleDefault //} var modalView string symbolToRepeat := "*" if m.title != "" { modalView = ui.ModalContainer. Width(modalWidth). Height(modalHeight). Align(lipgloss.Center, lipgloss.Center). Border(lipgloss.DoubleBorder()). BorderForeground(borderColor). Render(modalBody) //modalView = ui.ModalContainer. // Width(modalWidth). // Height(modalHeight). // BorderForeground(borderColor). // Render(modalBody) //repeatCount := modalWidth - utf8.RuneCountInString(m.title) //if repeatCount < 0 { // repeatCount = 0 //} //symbolFill := strings.Repeat(symbolToRepeat, repeatCount/2) //modalView = lipgloss.JoinVertical(lipgloss.Center, modalTitleStyle.Render(symbolFill+m.title+symbolFill), modalView) modalView = lipgloss.JoinVertical(lipgloss.Center, modalTitleStyle.Render(m.title), modalView) } else { modalView = ui.ModalContainer. Width(modalWidth). Height(modalHeight). BorderStyle(lipgloss.ASCIIBorder()). BorderForeground(borderColor). Render(modalBody) //modalView = modalStyle. // Width(modalWidth). // Height(modalHeight). // Padding(1, 3). // Border(lipgloss.DoubleBorder()). // Render(modalBody) } return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, modalView, lipgloss.WithWhitespaceChars(symbolToRepeat), lipgloss.WithWhitespaceForeground(ui.ColorGrey)) }