package setup import ( "bubble/setup/ui" "bubble/types" "fmt" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss/table" ) type FinalModel struct { variables []types.Variable table *table.Table viewport viewport.Model ready bool width int height int } func NewFinalModel(variables []types.Variable, width, height int) *FinalModel { m := &FinalModel{ variables: variables, width: width, height: height, ready: false, } if width > 0 && height > 0 { m.initViewport(width, height) } return m } func (m *FinalModel) Init() tea.Cmd { return nil } func (m *FinalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "up": m.viewport.ScrollUp(1) case "down": m.viewport.ScrollDown(1) case "left": m.viewport.ScrollLeft(1) case "right": m.viewport.ScrollRight(1) case "pageup": m.viewport.HalfPageUp() case "pagedown": m.viewport.HalfPageDown() case "home": m.viewport.GotoTop() case "end": m.viewport.GotoBottom() } case tea.WindowSizeMsg: m.height = msg.Height m.width = msg.Width m.initViewport(msg.Width, msg.Height) } m.viewport, cmd = m.viewport.Update(msg) return m, cmd } func (m *FinalModel) View() string { if !m.ready { return "Загрузка таблицы" } containerWidth, containerHeight, contentWidth, _ := ui.ContentSizes(m.width, m.height) m.rebuildTable(contentWidth) header := ui.FinalHeader. Width(contentWidth). Render("Итоговые значения переменных") footer := ui.FinalFooter. Width(contentWidth). Render(fmt.Sprintf("Строк: %d • q — выход • ↑↓ / jk — скролл", len(m.variables))) renderedTable := m.table.Render() content := lipgloss.NewStyle(). Align(lipgloss.Center). Render(renderedTable) m.viewport.SetContent(content) body := m.viewport.View() fullContent := lipgloss.JoinVertical(lipgloss.Top, header, body, footer) container := ui.AppContainer. Width(containerWidth). Height(containerHeight). Render(fullContent) return lipgloss.Place( m.width, m.height, lipgloss.Center, lipgloss.Center, container, lipgloss.WithWhitespaceChars("+"), lipgloss.WithWhitespaceForeground(ui.ColorError), ) } func (m *FinalModel) initViewport(width, height int) { _, _, contentWidth, contentHeight := ui.ContentSizes(width, height) tableWidth := contentWidth - 2*1 tableHeight := int(float64(contentHeight)*ui.TableWithRatio) - 2*1 m.viewport = viewport.New(contentWidth, tableHeight) //размеры для viewport в нем будет находиться таблица и перемещаться в зависимости от команд m.ready = true m.rebuildTable(tableWidth) m.viewport.SetContent(m.table.Render()) } func (m *FinalModel) rebuildTable(width int) { if len(m.variables) == 0 { m.table = table.New(). Border(lipgloss.RoundedBorder()). BorderStyle(ui.FinalTableBorder). StyleFunc(func(row, col int) lipgloss.Style { return ui.FinalTableHeader }).Headers("Нет переменных") return } variableNameColWidth := 15 defaultValueColWidth := 25 usedValueColWidth := width - variableNameColWidth - defaultValueColWidth - 4*1 //ширина таблицы - ширина первой колонки - ширина второй колонки - 4 границы (2 крайние, 2 между колонками) if usedValueColWidth < 30 { //если значение сильно меньше usedValueColWidth = 30 } t := table.New(). Width(variableNameColWidth+defaultValueColWidth+usedValueColWidth+4). Border(lipgloss.RoundedBorder()). BorderStyle(ui.FinalTableBorder). StyleFunc(func(row, col int) lipgloss.Style { var style lipgloss.Style if row == table.HeaderRow { return ui.FinalTableHeader } if row%2 == 0 { style = ui.FinalTableEvenRow } else { style = ui.FinalTableOddRow } switch col { case 0: return style.Width(variableNameColWidth).Align(lipgloss.Left) case 1: return style.Width(defaultValueColWidth).Align(lipgloss.Left) case 2: return style.Width(usedValueColWidth).Align(lipgloss.Left) } return style }). Headers("Variable name", "Default value", "Used value") for _, v := range m.variables { used := fmt.Sprintf("%v", v.Value) if used == "" { used = "<Пусто>" } t.Row(v.Name, v.Default, used) } m.table = t }