220 lines
8.0 KiB
Go
220 lines
8.0 KiB
Go
package ui
|
||
|
||
import (
|
||
"fmt"
|
||
|
||
"github.com/NicoNex/echotron/v3"
|
||
)
|
||
|
||
// PaginationConfig конфигурация для построения навигационного ряда пагинации
|
||
type PaginationConfig struct {
|
||
CurrentPage int
|
||
TotalPages int // Для API пагинации (из page.Pages)
|
||
TotalItems int // Для локальной пагинации (len(array))
|
||
ItemsPerPage int // Для расчета TotalPages из TotalItems
|
||
PrevCallback string // По умолчанию "prev"
|
||
NextCallback string // По умолчанию "next"
|
||
MiddleButtons []echotron.InlineKeyboardButton // Опциональные кнопки в центре (например, "+ Добавить")
|
||
}
|
||
|
||
// BuildNavigationRow создает навигационный ряд с динамической шириной.
|
||
// Показывает ряд только если страниц > 1.
|
||
// Возвращает nil если пагинация не нужна.
|
||
func BuildNavigationRow(config PaginationConfig) []echotron.InlineKeyboardButton {
|
||
// Вычисляем TotalPages из TotalItems если не задан
|
||
totalPages := config.TotalPages
|
||
if totalPages == 0 && config.TotalItems > 0 && config.ItemsPerPage > 0 {
|
||
totalPages = CalculatePages(config.TotalItems, config.ItemsPerPage)
|
||
}
|
||
|
||
// Устанавливаем значения по умолчанию для callbacks
|
||
prevCallback := config.PrevCallback
|
||
if prevCallback == "" {
|
||
prevCallback = "prev"
|
||
}
|
||
nextCallback := config.NextCallback
|
||
if nextCallback == "" {
|
||
nextCallback = "next"
|
||
}
|
||
|
||
var row []echotron.InlineKeyboardButton
|
||
|
||
// Левая стрелка (только если есть предыдущая страница)
|
||
if totalPages > 1 && config.CurrentPage > 0 {
|
||
row = append(row, echotron.InlineKeyboardButton{
|
||
Text: "←",
|
||
CallbackData: prevCallback,
|
||
})
|
||
}
|
||
|
||
// Средние кнопки (если есть)
|
||
if len(config.MiddleButtons) > 0 {
|
||
row = append(row, config.MiddleButtons...)
|
||
}
|
||
|
||
// Правая стрелка (только если есть следующая страница)
|
||
if totalPages > 1 && config.CurrentPage+1 < totalPages {
|
||
row = append(row, echotron.InlineKeyboardButton{
|
||
Text: "→",
|
||
CallbackData: nextCallback,
|
||
})
|
||
}
|
||
|
||
if len(row) == 0 {
|
||
return nil
|
||
}
|
||
|
||
return row
|
||
}
|
||
|
||
// CalculatePages вычисляет количество страниц из общего количества элементов
|
||
func CalculatePages(totalItems, itemsPerPage int) int {
|
||
if itemsPerPage <= 0 {
|
||
return 0
|
||
}
|
||
return (totalItems + itemsPerPage - 1) / itemsPerPage
|
||
}
|
||
|
||
// GetPageBounds возвращает start/end индексы для текущей страницы.
|
||
// Автоматически сбрасывает на первую страницу если currentPage выходит за границы.
|
||
func GetPageBounds(currentPage, itemsPerPage, totalItems int) (start, end int) {
|
||
start = currentPage * itemsPerPage
|
||
if start >= totalItems {
|
||
start = 0
|
||
}
|
||
end = start + itemsPerPage
|
||
if end > totalItems {
|
||
end = totalItems
|
||
}
|
||
return start, end
|
||
}
|
||
|
||
// FormatPageInfo форматирует текст индикатора страницы для заголовка
|
||
func FormatPageInfo(currentPage, totalPages int) string {
|
||
if totalPages <= 1 {
|
||
return ""
|
||
}
|
||
return fmt.Sprintf(" <i>(стр. %d/%d)</i>", currentPage+1, totalPages)
|
||
}
|
||
|
||
// ElementLayoutConfig конфигурация для автоматической раскладки элементов с пагинацией
|
||
type ElementLayoutConfig struct {
|
||
CurrentPage int // Текущая страница (0-indexed)
|
||
ItemsPerPage int // Элементов на странице
|
||
ItemsPerRow int // Элементов в одном ряду
|
||
EmptyButton *echotron.InlineKeyboardButton // Кнопка-заполнитель (опционально, по умолчанию " ")
|
||
}
|
||
|
||
// BuildElementRows автоматически раскладывает элементы с пагинацией.
|
||
// Принимает все элементы, возвращает ряды для текущей страницы с правильной раскладкой.
|
||
// Автоматически заполняет пустыми кнопками если страниц > 1 (для консистентности высоты).
|
||
// Возвращает 2D массив кнопок готовый к добавлению в клавиатуру.
|
||
//
|
||
// Пример использования:
|
||
//
|
||
// config := ui.ElementLayoutConfig{
|
||
// CurrentPage: 0,
|
||
// ItemsPerPage: 6,
|
||
// ItemsPerRow: 2,
|
||
// }
|
||
// rows := ui.BuildElementRows(config, allButtons)
|
||
// keyboard = append(keyboard, rows...)
|
||
func BuildElementRows(config ElementLayoutConfig, allItems []echotron.InlineKeyboardButton) [][]echotron.InlineKeyboardButton {
|
||
if len(allItems) == 0 {
|
||
return nil
|
||
}
|
||
|
||
// Параметры по умолчанию
|
||
if config.ItemsPerPage <= 0 {
|
||
config.ItemsPerPage = 6
|
||
}
|
||
if config.ItemsPerRow <= 0 {
|
||
config.ItemsPerRow = 2
|
||
}
|
||
|
||
// Кнопка-заполнитель по умолчанию
|
||
emptyButton := echotron.InlineKeyboardButton{Text: " ", CallbackData: "empty"}
|
||
if config.EmptyButton != nil {
|
||
emptyButton = *config.EmptyButton
|
||
}
|
||
|
||
// Вычисляем границы страницы
|
||
totalPages := CalculatePages(len(allItems), config.ItemsPerPage)
|
||
start, end := GetPageBounds(config.CurrentPage, config.ItemsPerPage, len(allItems))
|
||
|
||
// Получаем элементы текущей страницы
|
||
pageItems := allItems[start:end]
|
||
|
||
// Если страниц >= 2, заполняем до ItemsPerPage пустыми кнопками
|
||
// Это обеспечивает одинаковую высоту клавиатуры на всех страницах
|
||
if totalPages >= 2 {
|
||
for len(pageItems) < config.ItemsPerPage {
|
||
pageItems = append(pageItems, emptyButton)
|
||
}
|
||
}
|
||
|
||
// Раскладываем элементы по рядам
|
||
var rows [][]echotron.InlineKeyboardButton
|
||
for i := 0; i < len(pageItems); i += config.ItemsPerRow {
|
||
var row []echotron.InlineKeyboardButton
|
||
|
||
// Добавляем элементы в ряд
|
||
for j := 0; j < config.ItemsPerRow && i+j < len(pageItems); j++ {
|
||
row = append(row, pageItems[i+j])
|
||
}
|
||
|
||
// Если в ряду меньше элементов чем ItemsPerRow, заполняем пустыми
|
||
for len(row) < config.ItemsPerRow {
|
||
row = append(row, emptyButton)
|
||
}
|
||
|
||
rows = append(rows, row)
|
||
}
|
||
|
||
return rows
|
||
}
|
||
|
||
// BuildPageRows раскладывает элементы текущей страницы без собственной пагинации.
|
||
// Автоматически заполняет пустыми кнопками если страниц > 1 (для консистентности высоты).
|
||
func BuildPageRows(pageItems []echotron.InlineKeyboardButton, itemsPerRow, itemsPerPage, totalPages int) [][]echotron.InlineKeyboardButton {
|
||
if len(pageItems) == 0 {
|
||
return nil
|
||
}
|
||
|
||
// Параметры по умолчанию
|
||
if itemsPerPage <= 0 {
|
||
itemsPerPage = 6
|
||
}
|
||
if itemsPerRow <= 0 {
|
||
itemsPerRow = 2
|
||
}
|
||
|
||
// Кнопка-заполнитель по умолчанию
|
||
emptyButton := echotron.InlineKeyboardButton{Text: " ", CallbackData: "empty"}
|
||
|
||
// Если страниц >= 2, заполняем до ItemsPerPage пустыми кнопками
|
||
if totalPages >= 2 {
|
||
for len(pageItems) < itemsPerPage {
|
||
pageItems = append(pageItems, emptyButton)
|
||
}
|
||
}
|
||
|
||
// Раскладываем элементы по рядам
|
||
var rows [][]echotron.InlineKeyboardButton
|
||
for i := 0; i < len(pageItems); i += itemsPerRow {
|
||
var row []echotron.InlineKeyboardButton
|
||
|
||
for j := 0; j < itemsPerRow && i+j < len(pageItems); j++ {
|
||
row = append(row, pageItems[i+j])
|
||
}
|
||
|
||
for len(row) < itemsPerRow {
|
||
row = append(row, emptyButton)
|
||
}
|
||
|
||
rows = append(rows, row)
|
||
}
|
||
|
||
return rows
|
||
}
|