Paginação
Crie paginação estática, dinâmica ou híbrida e personalize completamente os botões de navegação.
StaticDynamicHybridCustom controls
Escolha o tipo certo#
| Tipo | Fonte | Melhor uso |
|---|---|---|
| Estática | Lista pronta de templates | Tutoriais, painéis e páginas com layout diferente |
| Dinâmica | Array dividido por pageSize | Usuários, logs, ranking e resultados de banco |
| Híbrida | Introdução fixa + array dinâmico | Catálogo com capa ou instruções antes dos itens |
Paginação estática#
Registre uma lista quando cada página já é conhecida durante o carregamento.
const page = (title: string, content: string) => new MessageBuilder() .addComponents( new ContainerBuilder() .setAccentColor(0xa571f4) .addTextDisplayComponents( new TextDisplayBuilder().setContent( '## ' + title + '' + content + 'Página ${pages.current}/${pages.total}' ) ) ) .template()Client.registerPaginator('guide-pages', [ { id: 'intro', name: 'Introdução', template: page('Introdução', 'Como usar o painel.') }, { id: 'config', name: 'Configuração', template: page('Configuração', 'Escolha seus canais.') }, { id: 'finish', name: 'Finalização', template: page('Tudo pronto', 'Revise e confirme.') }])Client.slash({ data: new SlashCommandBuilder() .setName('guide') .setDescription('Abre o guia paginado.'), execute: async interaction => { await interaction.paginatorRequest('guide-pages', { ephemeral: true, pageIndex: 0 }) }})Paginação dinâmica#
O paginator divide data, chama render para a fatia atual e injeta o contexto da página.
type UserRecord = { id: string name: string level: number}const users: UserRecord[] = await userRepository.list()const paginator = Paginator.dynamic({ id: 'users-pages', data: users, pageSize: 5, render: async (items, context) => { const rows = items.map((user, index) => (context.pageIndex * 5 + index + 1) + '. **' + user.name + '** — nível ' + user.level ) return new MessageBuilder() .addComponents( new ContainerBuilder().addTextDisplayComponents( new TextDisplayBuilder().setContent( '## Usuários' + rows.join('') + 'Página ' + context.page + '/' + context.pageCount ) ) ) .template() }})Client.registerPaginator('users-pages', paginator)| Contexto | Valor |
|---|---|
items | Itens da página atual |
page | Número amigável, começando em 1 |
pageIndex | Índice interno, começando em 0 |
pageCount | Quantidade total de páginas |
totalItems | Quantidade total de registros |
Dados novos a cada solicitação#
Use uma definição factory para consultar banco ou API quando o usuário abre a paginação, não durante o boot.
Client.registerPaginator('audit-pages', async context => { const guildId = context.guildId const records = guildId ? await auditRepository.list(guildId) : [] return Paginator.dynamic({ id: 'audit-pages', data: records, pageSize: 10, ownerId: context.ownerId, guildId, render: (items, page) => new MessageBuilder() .addComponents( new ContainerBuilder().addTextDisplayComponents( new TextDisplayBuilder().setContent( '## Auditoria' + items.map(item => '- ' + item.summary).join('') + '' + page.page + '/' + page.pageCount ) ) ) .template() })})Paginação híbrida#
A primeira página é fixa; as seguintes vêm de uma coleção dinâmica.
const intro = new MessageBuilder() .addComponents( new ContainerBuilder() .setAccentColor(0x6485ff) .addTextDisplayComponents( new TextDisplayBuilder().setContent( '## Catálogo NinenityUse **Próxima** para explorar os projetos.' ) ) ) .template()const paginator = Paginator.hybrid({ id: 'catalog-pages', intro, data: projects, pageSize: 3, render: (items, context) => new MessageBuilder() .addComponents( new ContainerBuilder().addTextDisplayComponents( new TextDisplayBuilder().setContent( items.map(project => '### ' + project.name + '' + project.summary).join('') + 'Página ' + context.page + '/' + context.pageCount ) ) ) .template()})Client.registerPaginator('catalog-pages', paginator)Botões de paginação personalizados#
Inclua uma action row no próprio template e marque a ação de cada botão. O runtime detecta os controles e não adiciona uma segunda linha.
const controls = new ActionRowBuilder<MessageActionRowComponentBuilder>() .addComponents( new ButtonBuilder() .setCustomId('guide-pages:first') .setLabel('Primeira') .setEmoji('⏮') .setStyle(ButtonStyle.Secondary) .setPaginationAction('first'), new ButtonBuilder() .setCustomId('guide-pages:back') .setLabel('Voltar') .setEmoji('◀') .setStyle(ButtonStyle.Secondary) .setPaginationAction('back'), new ButtonBuilder() .setCustomId('guide-pages:next') .setLabel('Próxima') .setEmoji('▶') .setStyle(ButtonStyle.Primary) .setPaginationAction('next'), new ButtonBuilder() .setCustomId('guide-pages:last') .setLabel('Última') .setEmoji('⏭') .setStyle(ButtonStyle.Secondary) .setPaginationAction('last') )const page = new MessageBuilder() .addComponents( new ContainerBuilder() .addTextDisplayComponents( new TextDisplayBuilder().setContent( 'Página ${pages.current} de ${pages.total}' ) ) .addActionRowComponents(controls) ) .template()- O prefixo do
customIddeve ser exatamente o ID registrado no paginator. backeprevioussão entendidos como a página anterior; no builder usesetPaginationAction('back').- Use
firstelastapenas quando a quantidade de páginas justifica esses atalhos. - O runtime hidrata os tokens
pages.currentepages.totalantes de enviar.
Personalizar rótulos automáticos#
Se as páginas não incluem controles, Paginator cria a linha e você pode trocar os rótulos.
const paginator = Paginator.static({ id: 'help-pages', pages: [introPage, commandsPage, settingsPage], controls: { first: 'Início', previous: 'Voltar', next: 'Avançar', last: 'Fim' }})Client.registerPaginator('help-pages', paginator)