Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,5 @@ typings/
.vscode/

# Autogenerated
src/index.mjs
src/index.mjs
package-lock.json
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</div>

# discord.js-pagination
A simple utility (or advanced - it's your choice) to paginate discord embeds. Built on discord.js@^13.0.0.
A simple utility (or advanced - it's your choice) to paginate discord embeds. Built on discord.js@^14.0.0 and discord-api-types@^0.37.9.

To see how the example paginations look, checkout the [example bot](example/README.md) (the readme has gifs)!

Expand Down Expand Up @@ -48,7 +48,7 @@ For the below examples, the pages can be constructed as per the [example bot](ex
```js
const pages = [];
for (let i = 0; i 10; i++) {
const pageEmbed = new MessageEmbed();
const pageEmbed = new EmbedBuilder();
pageEmbed
.setTitle(`This embed is index ${i}!`)
.setDescription(`That means it is page #${i + 1}`);
Expand All @@ -65,7 +65,7 @@ You will of course want to construct your own pages.
This allows pages to be navigated by reacting to a message.

```js
const { PaginatorEvents, ReactionPaginator } = require('@psibean/discord.js-pagination');
import { PaginatorEvents, ReactionPaginator } from '@psibean/discord.js-pagination';

const reactionPaginator = new ReactionPaginator(interaction, { pages })
.on(PaginatorEvents.COLLECT_ERROR, ({ error }) => console.log(error));
Expand All @@ -77,7 +77,7 @@ await reactionPaginator.send();
This allows pages to be navigated by clicking buttons.

```js
const { ButtonPaginator } = require('@psibean/discord.js-pagination');
import { ButtonPaginator } from '@psibean/discord.js-pagination';

const buttonPaginator = new ButtonPaginator(interaction, { pages });
await buttonPaginator.send();
Expand All @@ -90,7 +90,7 @@ This allows pages to be navigated using a select menu.
For the select pagination embed you'll need to supply an array of [MessageSelectOptions](https://discord.js.org/#/docs/main/stable/typedef/MessageSelectOption) to the pagination options via `selectOptions`. By default the pagination will map the value of the provided options to your pages index based on their ordering. Then the page change will be determined by the selected value and this mapping. If you want to map your select options and pages differently you can provide the `pagesMap` ({ selectOptions, paginator }) function which should return a dictionary. You'll then want to provide a custom `pageIndexResolver`.

```js
const { SelectPaginator } = require('@psibean/discord.js-pagination');
import { SelectPaginator } from '@psibean/discord.js-pagination';

// The default pagesMap will map these option values to the index
const selectOptions = [];
Expand Down Expand Up @@ -245,7 +245,7 @@ For the ActionRowPaginator, ButtonPaginator and SelectPaginator, defaults to:
{
idle: 6e4,
filter: ({ interaction, paginator }) =>
interaction.isMessageComponent() &&
interaction.type === InteractionType.MessageComponent; &&
interaction.component.customId.startsWith(paginator.customIdPrefix) &&
interaction.user === paginator.user &&
!interaction.user.bot,
Expand Down Expand Up @@ -328,7 +328,7 @@ Defaults to:

This is the function used to set the footer of each page. If not provided the embeds will use whatever footers were originally set on them.

(paginator): Promise&lt;string&gt; | string
(paginator): Promise&lt;EmbedFooterOptions&gt; | EmbedFooterOptions
- **paginator** : This is a reference to the paginator instance.

Defaults to undefined. Optional.
Expand All @@ -353,14 +353,14 @@ These options are common to the `ActionRowPaginator`, `ButtonPaginator`, and `Se

### messageActionRows

An array of messageActionRows (they will have their type set to ACTION_ROW).
An array of messageActionRows (they will have their type set to ComponentType.ActionRow).
Any components provided up front will have their customId generated.

Defaults to:
```
[
{
type: 'ACTION_ROW',
type: ComponentType.ActionRow,
components: [],
},
],
Expand Down Expand Up @@ -427,7 +427,7 @@ If a function:
Events can be imported by:

```
const { PaginatorEvents } = require('@psibean/discord.js-pagination');
import { PaginatorEvents } from '@psibean/discord.js-pagination';
```
And accessed by `PaginatorEvents#EventName`

Expand Down
6 changes: 3 additions & 3 deletions example/config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

module.exports = {
BOT_TOKEN: 'YOUR BOT_TOKEN HERE',
CLIENT_ID: 'YOUR CLIENT ID HERE',
GUILD_ID: 'YOUR GUILD ID HERE',
BOT_TOKEN: 'BOT_TOKEN_HERE',
CLIENT_ID: 'CLIENT_ID_HERE',
GUILD_ID: 'GUILD_ID_HERE',
};
9 changes: 5 additions & 4 deletions example/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"main": "src/index.js",
"type": "commonjs",
"scripts": {
"start": "node ."
},
"dependencies": {
"@discordjs/builders": "^0.4.0",
"@discordjs/builders": "^1.0.0",
"@discordjs/rest": "*",
"discord-api-types": "^0.22.0",
"discord.js": "13.2.0",
"node-fetch": "^2.6.1"
"discord-api-types": "^0.37.9",
"discord.js": "^14.3.0",
"node-fetch": "^2.6.7"
}
}
57 changes: 33 additions & 24 deletions example/src/commands/ActionRowPagination.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

const { SlashCommandBuilder } = require('@discordjs/builders');
const { Collection, MessageEmbed } = require('discord.js');
const { PaginatorEvents, ActionRowPaginator } = require('../../../src');
const { basicEndHandler, basicErrorHandler } = require('../util/Constants');
const { constructPokemonOptions, PokeAPI } = require('../util/PokeAPI');
const{ SlashCommandBuilder } = require('@discordjs/builders');
const{ Collection, EmbedBuilder, ComponentType, ButtonStyle } = require('discord.js');
const{ PaginatorEvents, ActionRowPaginator } = require('../../../src');
const{ basicEndHandler, basicErrorHandler } = require('../util/Constants');
const{ constructPokemonOptions, PokeAPI } = require('../util/PokeAPI');

module.exports = {
data: new SlashCommandBuilder()
Expand All @@ -18,33 +18,33 @@ module.exports = {
{
components: [
{
type: 'BUTTON',
type: ComponentType.Button,
emoji: '⏪',
label: 'start',
style: 'SECONDARY',
style: ButtonStyle.Secondary,
},
{
type: 'BUTTON',
type: ComponentType.Button,
label: `-${SELECT_LIMIT}`,
style: 'PRIMARY',
style: ButtonStyle.Primary,
},
{
type: 'BUTTON',
type: ComponentType.Button,
label: `+${SELECT_LIMIT}`,
style: 'PRIMARY',
style: ButtonStyle.Primary,
},
{
type: 'BUTTON',
type: ComponentType.Button,
emoji: '⏩',
label: 'end',
style: 'SECONDARY',
style: ButtonStyle.Secondary,
},
],
},
{
components: [
{
type: 'SELECT_MENU',
type: ComponentType.SelectMenu,
placeholder: 'Currently viewing #001 - #025',
},
],
Expand All @@ -61,7 +61,7 @@ module.exports = {

// eslint-disable-next-line no-shadow
const identifiersResolver = async ({ interaction, paginator }) => {
if (interaction.componentType === 'BUTTON') {
if (interaction.componentType === ComponentType.Button) {
let { selectOptionsIdentifier } = paginator.currentIdentifiers;
switch (interaction.component.label) {
case 'start':
Expand Down Expand Up @@ -94,7 +94,7 @@ module.exports = {
...paginator.currentIdentifiers,
selectOptionsIdentifier,
};
} else if (interaction.componentType === 'SELECT_MENU') {
} else if (interaction.componentType === ComponentType.SelectMenu) {
return {
...paginator.currentIdentifiers,
pageIdentifier: interaction.values[0],
Expand All @@ -109,19 +109,28 @@ module.exports = {
if (newPageIdentifier !== currentPageIdentifier) {
// Pokemon name
const pokemonResult = await PokeAPI.getPokemon(newPageIdentifier);
const newEmbed = new MessageEmbed()
const newEmbed = new EmbedBuilder()
.setTitle(`Pokedex #${`${pokemonResult.id}`.padStart(3, '0')} - ${newPageIdentifier}`)
.setDescription(`Viewing ${newPageIdentifier}`)
.setThumbnail(pokemonResult.sprites.front_default)
.addField('Types', pokemonResult.types.map(typeObject => typeObject.type.name).join(', '), true)
.addField(
'Abilities',
pokemonResult.abilities.map(abilityObject => abilityObject.ability.name).join(', '),
false,
);
.addFields([
{
name: 'Types',
value: pokemonResult.types.map(typeObject => typeObject.type.name).join(', '),
inline: true,
},
{
name: 'Abilities',
value: pokemonResult.abilities.map(abilityObject => abilityObject.ability.name).join(', '),
inline: false,
},
]);
const pokemonEmbedFields = [];
pokemonResult.stats.forEach(statObject => {
newEmbed.addField(statObject.stat.name, `${statObject.base_stat}`, true);

newEmbed.push({ name: statObject.stat.name, value: `${statObject.base_stat}`, inline: true});
});
newEmbed.addFields(pokemonEmbedFields);
return newEmbed;
}
return paginator.currentPage;
Expand Down
56 changes: 32 additions & 24 deletions example/src/commands/AdvancedPagination.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const { SlashCommandBuilder } = require('@discordjs/builders');
const { Collection, MessageEmbed } = require('discord.js');
const { Collection, EmbedBuilder, ComponentType, ButtonStyle, SelectMenuOptionBuilder } = require('discord.js');
const { PaginatorEvents, ActionRowPaginator } = require('../../../src');
const { basicEndHandler, basicErrorHandler } = require('../util/Constants');
const { constructPokemonOptions, PokeAPI } = require('../util/PokeAPI');
Expand Down Expand Up @@ -40,7 +40,7 @@ module.exports = {
{
components: [
{
type: 'SELECT_MENU',
type: ComponentType.SelectMenu,
placeholder: 'Select a type to filter the Pokemon',
options: pokemonTypeSelectOptions,
customId: 'pokemon-type',
Expand All @@ -52,7 +52,7 @@ module.exports = {
{
components: [
{
type: 'SELECT_MENU',
type: ComponentType.SelectMenu,
placeholder: `Currently viewing #001 - #025 of ${totalPokemonOfType}`,
options: pokemonSelectOptions.get(INITIAL_SELECT_IDENTIFIER),
customId: 'pokemon-select',
Expand All @@ -64,26 +64,26 @@ module.exports = {
{
components: [
{
type: 'BUTTON',
type: ComponentType.Button,
emoji: '⏪',
label: 'start',
style: 'SECONDARY',
style: ButtonStyle.Secondary,
},
{
type: 'BUTTON',
type: ComponentType.Button,
label: `-${SELECT_LIMIT}`,
style: 'PRIMARY',
style: ButtonStyle.Primary,
},
{
type: 'BUTTON',
type: ComponentType.Button,
label: `+${SELECT_LIMIT}`,
style: 'PRIMARY',
style: ButtonStyle.Primary,
},
{
type: 'BUTTON',
type: ComponentType.Button,
emoji: '⏩',
label: 'end',
style: 'SECONDARY',
style: ButtonStyle.Secondary,
},
],
},
Expand Down Expand Up @@ -159,7 +159,7 @@ module.exports = {
isAll
? pokemonList
: pokemonList.slice(INITIAL_SELECT_IDENTIFIER, SELECT_LIMIT).map(pokemonEntry => pokemonEntry.pokemon),
),
).map(option => option),
);
return { selectOptionsIdentifier: INITIAL_SELECT_IDENTIFIER, pokemonTypeIdentifier: pokemonType };
} catch (error) {
Expand All @@ -171,9 +171,9 @@ module.exports = {
// eslint-disable-next-line no-shadow
const identifiersResolver = async ({ interaction, paginator }) => {
let newIdentifiers = {};
if (interaction.componentType === 'BUTTON') {
if (interaction.componentType === ComponentType.Button) {
newIdentifiers = await handleButtonIdentifier(interaction.component.label, paginator);
} else if (interaction.componentType === 'SELECT_MENU') {
} else if (interaction.componentType === ComponentType.SelectMenu) {
if (interaction.component.customId.includes('pokemon-type')) {
const { pokemonTypeIdentifier: currentPokemonType } = paginator.currentIdentifiers;
const newPokemonType = interaction.values[0];
Expand All @@ -196,20 +196,28 @@ module.exports = {
try {
// Pokemon name
const pokemonResult = await PokeAPI.getPokemon(newPageIdentifier);
const newEmbed = new MessageEmbed()
const newEmbed = new EmbedBuilder()
.setTitle(`Pokedex #${`${pokemonResult.id}`.padStart(3, '0')} - ${newPageIdentifier}`)
.setDescription(`Viewing ${newPageIdentifier}`)
.setThumbnail(pokemonResult.sprites.front_default)
.setFooter('Information fetched from https://pokeapi.co/')
.addField('Types', pokemonResult.types.map(typeObject => typeObject.type.name).join(', '), true)
.addField(
'Abilities',
pokemonResult.abilities.map(abilityObject => abilityObject.ability.name).join(', '),
false,
);
.setFooter({ text: 'Information fetched from https://pokeapi.co/' })
.addFields([
{
name: 'Types',
value: pokemonResult.types.map(typeObject => typeObject.type.name).join(', '),
inline: true,
},
{
name: 'Abilities',
value: pokemonResult.abilities.map(abilityObject => abilityObject.ability.name).join(', '),
inline: false,
},
]);
const pokemonEmbedFields = [];
pokemonResult.stats.forEach(statObject => {
newEmbed.addField(statObject.stat.name, `${statObject.base_stat}`, true);
pokemonEmbedFields.push({ name: statObject.stat.name, value: `${statObject.base_stat}`, inline: true});
});
newEmbed.addFields(pokemonEmbedFields);
return newEmbed;
} catch (error) {
console.log(`Error in pageEmbedResolver\n${error}`);
Expand Down Expand Up @@ -237,7 +245,7 @@ module.exports = {
newSelectOptionsIdentifier !== currentSelectOptionsIdentifier ||
newPokemonTypeIdentifier !== currentPokemonTypeIdentifier
) {
paginator.getComponent(1, 0).options = pokemonSelectOptions.get(newSelectOptionsIdentifier);
paginator.getComponent(1, 0).setOptions(pokemonSelectOptions.get(newSelectOptionsIdentifier));
const endOffset = newSelectOptionsIdentifier * SELECT_LIMIT + SELECT_LIMIT;
paginator.getComponent(1, 0).placeholder = `Currently viewing #${`${
newSelectOptionsIdentifier * SELECT_LIMIT + 1
Expand Down
Loading