npm package: @novelbrasil/discord.js-embed-menu
Description: Easily create Discord.js embed menus with reactions and unlimited customisable pages.
Author: eilex
Latest version: 1.2.7

<div align="center">
  <p>
    <a href="https://github.com/NovelBrasil/discord.js-embed-menu"><img src="https://img.shields.io/npm/v/@novelbrasil/discord.js-embed-menu" alt="NPM version" /></a>
    <a href="https://discord.js.org"><img src="https://img.shields.io/npm/dependency-version/@novelbrasil/discord.js-embed-menu/discord.js" alt="NPM (prod) dependency version" /></a>
  </p>
</div>

## Table of contents

- [About](#about)
- [Installation](#installation)
- [Example usage](#example-usage)
- [Special destinations](#special-destinations)
- [Contributing](#contributing)
- [Links](#links)

## About

This discord.js-embed-menu is a fork of the [original repository](https://github.com/eilex/discord.js-embed-menu). This version features support for components in addition to working for recent versions of [discord.js](https://discord.js.org/)

- Channel and direct message menu
- Remove other users reactions
- Pagination system
- Mention user
- Timeout and auto delete
- Support to followUp and reply interaction
- Support to button component

## Installation

**Node.js 16.11.0 or newer is required.**  

```shell
npm i @novelbrasil/discord.js-embed-menu --save
```
```shell
pnpm add @novelbrasil/discord.js-embed-menu
```

## Example usage

### General Use (Reaction Example)

```js
/* Import all the usual stuff. */
import { Client, MessageEmbed } from 'discord.js';
import { DiscordEmbedMenu } from '@novelbrasil/discord.js-embed-menu';

const client = new Client();

/* Run this code every time a new message is sent. */
client.on('message', message => {
    if (message.content === '!menu') {
        /*
         * The menu class takes 3 mandatory parameters and 5 optional parameters. 
         * 1) A channel to send the menu to, it can be null so it will send DMs to the user.
         * 2) A user to give control over the navigation or send DM if channel is null.
         * 3) An array of Page objects or DiscordEmbedMenuPage, each being a unique page of the menu.
         * 4) How long, in milliseconds, you want the menu to wait for new reactions, it can be null so it will wait for ever (not recommended).
         * 5) If it shall or not delete the message on menu timeout.
         * 6) If it shall mention menu's user on discord channels.
         * 7) If it shall keep last user reaction when you call stop.
         * 8) The message to be displayed when it's loading the menu page.
         */
        let menu = new DiscordEmbedMenu(message.channel, message.author, [
            {
                /*
                 * A page object consists of three items:
                 * 1) A name. This is used as a unique destination name for reactions.
                 * 2) Some content. This is a rich embed. You can use {object: formatting} or .functionFormatting() for embeds. Whichever you prefer.
                 * 3) A set of reactions, linked to either a page destination or a function.* (See example pages)
                 * 
                 * Reactions can be emojis or custom emote IDs, and reaction destinations can be either the names
                 * of pages, () => { functions }, or special destination names. See below for a list of these.
                 */

                /* You can call pages whatever you like. The first in the array is always loaded first. */
                name: 'main',
                content: new MessageEmbed({
                    title: 'Main menu',
                    description: 'Please chose an action',
                    fields: [
                        {
                            name: "📝 Sub menu",
                            value: "Goes to another menu.",
                            inline: false
                        },
                        {
                            name: "✉️ Direct message",
                            value: "Sends a direct message.",
                            inline: false
                        },
                        {
                            name: "❌ Close",
                            value: "Close the menu.",
                            inline: false
                        }
                    ]
                }),
                reactions: {
                    '📝': 'sub-menu',
                    '✉️': async (menu) => {
                        menu.user.send(`Hello dear ${menu.user.username}.`);
                    },
                    '❌': 'delete'
                }
            },
            {
                name: 'sub-menu',
                content: new MessageEmbed({
                    title: 'Sub menu',
                    description: 'This is another page.',
                    fields: [
                        {
                            name: "⬅️ Back",
                            value: "Go backwards.",
                            inline: false
                        },
                        {
                            name: "❌ Close",
                            value: "Close the menu.",
                            inline: false
                        }
                    ]
                }),
                reactions: {
                    '⬅️': 'main',
                    '❌': 'delete'
                }
            }
        ]);

        /* Run Menu.start() when you're ready to send the menu. */
        menu.start();

        /* The menu also has two events you can use.
         * The "page-changing" event fires just before a new page is sent.
         * The "page-changed" event fires after the new page is sent.
         */
        menu.on('page-changing', (oldPageIndex, oldPage, newPageIndex, newPage) => {
            console.log(`Menu is going from "${oldPage.content.title}" (${oldPageIndex}) to "${newPage.content.title}" (${newPageIndex})`);
        });

        menu.on('page-changed', (pageIndex, page) => {
            console.log(`Menu is now on "${page.content.title}" (${pageIndex})`);
        });
    }
});

client.login("Get your bot's oauth token at https://discord.com/developers/applications");
```

### Button Component Example

```js
/* ... */
const menu = new DiscordEmbedMenu(interaction, [
    {
        name: `main`,
        content: new EmbedBuilder({
        title: `Main menu`,
        description: `Please chose an action`,
            fields: [
                {
                    name: `📝 Sub menu`,
                    value: `Goes to another menu.`,
                    inline: false
                },
                {
                    name: `❌ Close`,
                    value: `Close the menu.`,
                    inline: false
                }
            ]
        }),
        buttons: {
            submenu: {
                action: `submenu`,
                button: new ButtonBuilder().setCustomId(`submenu`)
                    .setLabel(`Sub Menu`)
                    .setEmoji(`📝`)
                    .setStyle(ButtonStyle.Primary)
            },
            delete: {
                action: `delete`,
                button: new ButtonBuilder().setCustomId(`delete`)
                    .setLabel(`Close`)
                    .setEmoji(`❌`)
                    .setStyle(ButtonStyle.Danger)
            }
        },
    },
    {
        name: `submenu`,
        content: new EmbedBuilder({
        title: `Sub menu`,
        description: `Please chose an action`,
        fields: [
            {
                name: `📝 Sub menu`,
                value: `Goes to another menu.`,
                inline: false
            },
            {
                name: `❌ Close`,
                value: `Close the menu.`,
                inline: false
            }
        ]
    }),
    buttons: {
        main: {
            action: `main`,
            button: new ButtonBuilder().setCustomId(`main`)
                    .setLabel(`Back`)
                    .setEmoji(`⬅️`)
                    .setStyle(ButtonStyle.Primary)
            },
        delete: {
            action: `delete`,
            button: new ButtonBuilder().setCustomId(`delete`)
                    .setLabel(`Close`)
                    .setEmoji(`❌`)
                    .setStyle(ButtonStyle.Danger)
            }
        },
    },
]);

/* Run Menu.start() when you're ready to send the menu. */
/* Options: send, followUp, reply */
/* Choose only one option */
menu.start({ followUp: true });

/* ... */
```

## Special Destinations

Discord.js-embed-menu comes with 6 pre-defined destinations with specific uses.

| Destination 	| Function                                                      	|
|-------------	|---------------------------------------------------------------	|
| first       	| Goes to the first page in the array.                          	|
| last        	| Goes to the last page in the array.                           	|
| previous    	| Goes to the previous page in the array.                       	|
| next        	| Goes to the next page in the array.                           	|
| stop        	| Removes reactions from the embed and stops updating the menu. 	|
| delete      	| Stops the menu and deletes the message.                       	|

Calling a page one of these wouldn't work, it prioritizes special destinations.

## Contributing

Before creating an issue, please ensure that it hasn't already been reported/suggested.

## Links

- [discord.js](https://github.com/discordjs/discord.js)
- [discord.js-menu](https://github.com/eilex/discord.js-embed-menu)