mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-13 01:53:30 +01:00
feat(guide): port legacy guide (#10938)
* feat: initial attempt at porting legacy guide * feat: completion of legacy guide backport * chore: lockfile shenanigans * fix: handle svgs * fix: replace svg with mermaid integration * chore: format * chore: remove unnecssary bullet * chore: cleanup code highlights * chore: explicit return * chore: move display components after interactive components in sidebar * chore: voice * top link should be installation * add docs link to sidebar * feat: subguide-based accent styles * chore: don't list faq twice * chore: mention display components in interactive components * fix: remove unoccs/order rule from guide * chore: redirect to legacy guide instead of /guide root * refactor: use `<kbd>` * refactor: more kbd use * Update apps/guide/content/docs/legacy/app-creation/handling-events.mdx Co-authored-by: Naiyar <137700126+imnaiyar@users.noreply.github.com> * chore: fix typos Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> * chore: fix typos * chore: fix links regarding secret stores across coding platforms * chore: fix typo * chore: link node method directly Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> * chore: typos Co-authored-by: Vlad Frangu <me@vladfrangu.dev> * chore: typo Co-authored-by: Vlad Frangu <me@vladfrangu.dev> * fix: prevent v14 changes from being listed twice * chore: prefer relative links * chore: missed link conversion * chore: missed link conversion * chore: fix link * chore: remove legacy code highlight markers * chore: rephrase and extend contributing guidelines * feat(setup): suggest cli flag over dotenv package * chore: move introduction in sidebar better navigation experience if the 'next page' in intro refers to getting started vs. updating/faq * fix: replace outdated link * fix: update voice dependencies * chore: update node install instructions * fix: list in missing access callout * chore: match bun env file format * chore: restore ffmpeg disclaimer * fix: lockfile conflict * chore: action row typo Co-authored-by: Vlad Frangu <me@vladfrangu.dev> * chore: no longer use at-next for pino --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> Co-authored-by: Naiyar <137700126+imnaiyar@users.noreply.github.com> Co-authored-by: Vlad Frangu <me@vladfrangu.dev>
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Action rows
|
||||
---
|
||||
|
||||
Action rows are a layout component with five "slots" that can be filled with other components. At the time of writing this guide, buttons take up one slot and select menus take up five "slots". Accordingly, each `ActionRow` can hold up to 5 buttons or a single select menu. A message can have up to five rows without providing the `IsComponentsV2` message flag. If you want to place buttons or action rows into the message body, they have to be wrapped inside rows.
|
||||
|
||||
<Callout>
|
||||
Read our guide section on [display components](../popular-topics/display-components) if you want to learn more about
|
||||
those.
|
||||
</Callout>
|
||||
|
||||
## Building action rows
|
||||
|
||||
To create an action row, use the `ActionRowBuilder` class and the `ActionRowBuilder#addComponents` method to add buttons or a select menu.
|
||||
|
||||
```js
|
||||
const row = new ActionRowBuilder().addComponents(component);
|
||||
```
|
||||
|
||||
<Callout>
|
||||
If you're using TypeScript, you'll need to specify the type of components your action row holds. This can be done by specifying the component builder you will add to it using a generic parameter in `ActionRowBuilder`.
|
||||
|
||||
```js
|
||||
new ActionRowBuilder() // [!code --]
|
||||
new ActionRowBuilder<ButtonBuilder>() // [!code ++]
|
||||
```
|
||||
|
||||
</Callout>
|
||||
|
||||
## Sending action rows
|
||||
|
||||
Once one or many components are inside your row(s), send them in the `components` property of your `InteractionReplyOptions` (extends `BaseMessageOptions`).
|
||||
|
||||
```js
|
||||
const row = new ActionRowBuilder().addComponents(component);
|
||||
|
||||
await interaction.reply({ components: [row] });
|
||||
```
|
||||
|
||||
To learn how to create the buttons and select menus that will go inside your row, including more detailed examples on how you might use them, continue on to the other pages in this section.
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
title: Buttons
|
||||
---
|
||||
|
||||
The first type of interactive component we'll cover creating is a Button. Buttons are available in a variety of styles and can be used to provide permanent interfaces, temporary confirmation workflows, and other forms of additional interaction with your bot.
|
||||
|
||||
<Callout>
|
||||
This page is a follow-up to the [slash commands](../slash-commands/advanced-creation) section and [action
|
||||
rows](./action-rows) page. Please carefully read those pages first so that you can understand the methods used here.
|
||||
</Callout>
|
||||
|
||||
## Building buttons
|
||||
|
||||
Buttons are one of the `MessageComponent` classes, which can be sent via messages or interaction responses.
|
||||
|
||||
For this example, you're going to expand on the `ban` command that was previously covered on the [parsing options](../slash-commands/parsing-options) page with a confirmation workflow.
|
||||
|
||||
To create your buttons, use the `ButtonBuilder` class, defining at least the `customId`, `style` and `label`.
|
||||
|
||||
```js
|
||||
const { ButtonBuilder, ButtonStyle, SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
// data: new SlashCommandBuilder()...
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('target');
|
||||
const reason = interaction.options.getString('reason') ?? 'No reason provided';
|
||||
|
||||
const confirm = new ButtonBuilder().setCustomId('confirm').setLabel('Confirm Ban').setStyle(ButtonStyle.Danger);
|
||||
|
||||
const cancel = new ButtonBuilder().setCustomId('cancel').setLabel('Cancel').setStyle(ButtonStyle.Secondary);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
<Callout>
|
||||
The custom id is a developer-defined string of up to 100 characters. Use this field to ensure you can uniquely define
|
||||
all incoming interactions.
|
||||
</Callout>
|
||||
|
||||
## Sending buttons
|
||||
|
||||
To send your buttons, create an action row and add the buttons as components. Then, send the row in the `components` property of `InteractionReplyOptions` (extends `BaseMessageOptions`).
|
||||
|
||||
```js
|
||||
const { ActionRowBuilder, ButtonBuilder, ButtonStyle, SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
// data: new SlashCommandBuilder()...
|
||||
async execute(interaction) {
|
||||
const target = interaction.options.getUser('target');
|
||||
const reason = interaction.options.getString('reason') ?? 'No reason provided';
|
||||
|
||||
const confirm = new ButtonBuilder().setCustomId('confirm').setLabel('Confirm Ban').setStyle(ButtonStyle.Danger);
|
||||
|
||||
const cancel = new ButtonBuilder().setCustomId('cancel').setLabel('Cancel').setStyle(ButtonStyle.Secondary);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(cancel, confirm);
|
||||
|
||||
await interaction.reply({
|
||||
content: `Are you sure you want to ban ${target} for reason: ${reason}?`,
|
||||
components: [row],
|
||||
});
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Restart your bot and then send the command to a channel your bot has access to. If all goes well, you should see something like this:
|
||||
|
||||
## Button styles
|
||||
|
||||
You'll notice in the above example that two different styles of buttons have been used, the grey Secondary style and the red Danger style. These were chosen specifically to support good UI/UX principles. In total, there are five button styles that can be used as appropriate to the action of the button:
|
||||
|
||||
- `Primary` style buttons are blue. These are suitable for most general purpose actions, where it's the primary or most significant action expected.
|
||||
- `Secondary` style buttons are grey. Use these for less important actions like the "Cancel" button in the example above.
|
||||
- `Success` style buttons are green. Similar to the Primary button, these are a good choice for "positive" confirmation actions.
|
||||
- `Danger` style buttons are red. Where the action being confirmed is "destructive", such a ban or delete, using a red button helps alert the user to the risk of the action.
|
||||
- `Link` style buttons are also grey, but are tagged with the "external link" symbol. These buttons will open the provided link in the browser without sending an interaction to the bot.
|
||||
|
||||
## Link buttons
|
||||
|
||||
Link buttons are a little different to the other styles. `Link` buttons **must** have a `url`, **cannot** have a `customId` and **do not** send an interaction event when clicked.
|
||||
|
||||
```js
|
||||
const button = new ButtonBuilder()
|
||||
.setLabel('discord.js docs')
|
||||
.setURL('https://discord.js.org') // [!code word:setURL]
|
||||
.setStyle(ButtonStyle.Link); // [!code word:Link]
|
||||
```
|
||||
|
||||
## Disabled buttons
|
||||
|
||||
If you want to prevent a button from being used, but not remove it from the message, you can disable it with the `ButtonBuilder#setDisabled` method:
|
||||
|
||||
```js
|
||||
const button = new ButtonBuilder()
|
||||
.setCustomId('disabled')
|
||||
.setLabel('Click me?')
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setDisabled(true); // [!code word:setDisabled]
|
||||
```
|
||||
|
||||
## Emoji buttons
|
||||
|
||||
If you want to use a guild emoji within a `ButtonBuilder`, you can use the `ButtonBuilder#setEmoji` method:
|
||||
|
||||
```js
|
||||
const button = new ButtonBuilder()
|
||||
.setCustomId('primary')
|
||||
.setLabel('Primary')
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setEmoji('123456789012345678'); // [!code word:setEmoji]
|
||||
```
|
||||
|
||||
#### Next steps
|
||||
|
||||
<Callout>
|
||||
Buttons can also be set as an accessory in section components. Check the guide section on [display
|
||||
components](../popular-topics/display-components) if you want to find out more about this.
|
||||
</Callout>
|
||||
|
||||
That's everything you need to know about building and sending buttons! From here you can learn about the other type of message component, [select menus](./select-menus), or have your bot start listening to [component interactions](./interactions) from your buttons.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,213 @@
|
||||
---
|
||||
title: Interactions
|
||||
---
|
||||
|
||||
## Component interactions
|
||||
|
||||
Every button click or select menu selection on a component sent by your bot fires an `interaction`, triggering the `Client#interactionCreate` event. How you decide to handle this will likely depend on the purpose of the components. Options include:
|
||||
|
||||
- Waiting for a single interaction via `awaitMessageComponent()` on a channel or a message.
|
||||
- Listening for multiple interactions over a period of time using an `InteractionCollector`.
|
||||
- Creating a permanent component handler in the `Client#interactionCreate` event.
|
||||
|
||||
<Callout>
|
||||
This page is a follow-up to the [slash commands](../slash-commands/advanced-creation) section, and assumes you have
|
||||
created either [buttons](./buttons) or [select menus](./select-menus) as detailed in this guide. Please carefully read
|
||||
those pages first so that you can understand the methods used here.
|
||||
</Callout>
|
||||
|
||||
## Responding to component interactions
|
||||
|
||||
As with all other interactions message components interactions require a response within 3 seconds, else Discord will treat them as failed.
|
||||
|
||||
Like slash commands, all types of message component interactions support the `reply()`, `deferReply()`, `editReply()` and `followUp()` methods, with the option for these responses to be ephemeral. These function identically to how they do for slash commands, so refer to the page on [slash command response methods](../slash-commands/response-methods) for information on those.
|
||||
|
||||
Component interactions also support two additional methods of response, detailed below and demonstrated in examples later on the page.
|
||||
|
||||
### Updates
|
||||
|
||||
Responding to a component interaction via the `update()` method acknowledges the interaction by editing the message on which the component was attached. This method should be preferred to calling `editReply()` on the original interaction which sent the components. Like `editReply()`, `update()` cannot be used to change the ephemeral state of a message.
|
||||
|
||||
Once `update()` has been called, future messages can be sent by calling `followUp()` or edits can be made by calling `editReply()` on the component interaction.
|
||||
|
||||
### Deferred updates
|
||||
|
||||
Responding to a component interaction via the `deferUpdate()` method acknowledges the interaction and resets the message state. This method can be used to suppress the need for further responses, however it's encouraged to provide meaningful feedback to users via an `update()` or ephemeral `reply()` at least.
|
||||
|
||||
Once `deferUpdate()` has been called, future messages can be sent by calling `followUp()` or edits can be made by calling `editReply()` on the component interaction.
|
||||
|
||||
## Awaiting components
|
||||
|
||||
If you followed our [buttons](./buttons) guide, the confirmation workflow for the `ban` command is a good example of a situation where your bot is expecting to receive a single response, from either the Confirm or Cancel button.
|
||||
|
||||
Begin by adding `withResponse` to the options, and then calling `Message#awaitMessageComponent` on the message. This method returns a [Promise](../additional-info/async-await) that resolves when any interaction passes its filter (if one is provided), or throws if none are received before the timeout. If this happens, remove the components and notify the user.
|
||||
|
||||
```js title="commands/moderation/ban.js"
|
||||
const response = await interaction.reply({
|
||||
content: `Are you sure you want to ban ${target.username} for reason: ${reason}?`,
|
||||
components: [row],
|
||||
// [!code ++]
|
||||
withResponse: true,
|
||||
});
|
||||
|
||||
// [!code ++:7]
|
||||
const collectorFilter = (i) => i.user.id === interaction.user.id;
|
||||
|
||||
try {
|
||||
const confirmation = await response.resource.message.awaitMessageComponent({ filter: collectorFilter, time: 60_000 });
|
||||
} catch {
|
||||
await interaction.editReply({ content: 'Confirmation not received within 1 minute, cancelling', components: [] });
|
||||
}
|
||||
```
|
||||
|
||||
<Callout>
|
||||
The filter applied here ensures that only the user who triggered the original interaction can use the buttons.
|
||||
</Callout>
|
||||
|
||||
With the confirmation collected, check which button was clicked and perform the appropriate action.
|
||||
|
||||
```js
|
||||
const response = await interaction.reply({
|
||||
content: `Are you sure you want to ban ${target.username} for reason: ${reason}?`,
|
||||
components: [row],
|
||||
withResponse: true,
|
||||
});
|
||||
|
||||
const collectorFilter = (i) => i.user.id === interaction.user.id;
|
||||
try {
|
||||
// [!code focus:8] [!code word:customId]
|
||||
const confirmation = await response.resource.message.awaitMessageComponent({ filter: collectorFilter, time: 60_000 });
|
||||
|
||||
if (confirmation.customId === 'confirm') {
|
||||
await interaction.guild.members.ban(target);
|
||||
await confirmation.update({ content: `${target.username} has been banned for reason: ${reason}`, components: [] });
|
||||
} else if (confirmation.customId === 'cancel') {
|
||||
await confirmation.update({ content: 'Action cancelled', components: [] });
|
||||
}
|
||||
} catch {
|
||||
await interaction.editReply({ content: 'Confirmation not received within 1 minute, cancelling', components: [] });
|
||||
}
|
||||
```
|
||||
|
||||
## Component collectors
|
||||
|
||||
For situations where you want to collect multiple interactions, the Collector approach is better suited than awaiting singular interactions. Following on from the [select menus](./select-menus) guide, you're going to extend that example to use an `InteractionCollector` to listen for multiple `StringSelectMenuInteraction`.
|
||||
|
||||
Begin by adding `withResponse` to the options, and then calling `Message#createMessageComponentCollector` on this instance. This method returns an InteractionCollector that will fire its `InteractionCollector#collect` event whenever an interaction passes its filter (if one is provided).
|
||||
|
||||
In the `collect` event, each interaction is a `StringSelectMenuInteraction` thanks to the `componentType: ComponentType.StringSelect` option provided to the collector (and because that was the only type of component in the message). The selected value(s) are available via the `StringSelectMenuInteraction#values` property.
|
||||
|
||||
```js
|
||||
const response = await interaction.reply({
|
||||
content: 'Choose your starter!',
|
||||
components: [row],
|
||||
withResponse: true, // [!code ++]
|
||||
});
|
||||
|
||||
// [!code ++:9]
|
||||
const collector = response.resource.message.createMessageComponentCollector({
|
||||
componentType: ComponentType.StringSelect,
|
||||
time: 3_600_000, // 1 hour
|
||||
});
|
||||
|
||||
collector.on('collect', async (i) => {
|
||||
const selection = i.values[0];
|
||||
await i.reply(`${i.user} has selected ${selection}!`);
|
||||
});
|
||||
```
|
||||
|
||||
## The Client#interactionCreate event
|
||||
|
||||
Third and finally, you may wish to have a listener setup to respond to permanent button or select menu features of your guild. For this, returning to the `Client#interactionCreate` event is the best approach.
|
||||
|
||||
If your event handling has been setup in multiple files as per our [event handling](../app-creation/handling-events) guide, you should already have an `events/interactionCreate.js` file that looks something like this.
|
||||
|
||||
```js title="events/interactionCreate.js"
|
||||
const { Events } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
name: Events.InteractionCreate,
|
||||
async execute(interaction) {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const command = interaction.client.commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
} catch (error) {
|
||||
console.error(`Error executing ${interaction.commandName}`);
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The way this was previously set up returns from the `execute` function whenever it encounters an interaction that is not a `ChatInputCommandInteraction`, as shown on the highlighted line above. The first change that needs to be made is to invert this logic, without actually changing the functionality.
|
||||
|
||||
```js title="events/interactionCreate.js"
|
||||
const { Events } = require('discord.js');
|
||||
|
||||
// [!code focus:22]
|
||||
module.exports = {
|
||||
name: Events.InteractionCreate,
|
||||
async execute(interaction) {
|
||||
if (!interaction.isChatInputCommand()) return; // [!code --]
|
||||
// [!code ++:15]
|
||||
if (interaction.isChatInputCommand()) {
|
||||
const command = interaction.client.commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
} catch (error) {
|
||||
console.error(`Error executing ${interaction.commandName}`);
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Now that the logic is setup to run code when something _is_ a `ChatInputCommandInteraction`, rather than to stop and exit when it isn't, you can add handling for additional interaction types via simple `if...else` logic.
|
||||
|
||||
```js
|
||||
const { Events } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
name: Events.InteractionCreate,
|
||||
// [!code focus]
|
||||
async execute(interaction) {
|
||||
// [!code focus]
|
||||
if (interaction.isChatInputCommand()) {
|
||||
const command = interaction.client.commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
} catch (error) {
|
||||
console.error(`Error executing ${interaction.commandName}`);
|
||||
console.error(error);
|
||||
}
|
||||
// [!code focus:5] [!code ++:5]
|
||||
} else if (interaction.isButton()) {
|
||||
// respond to the button
|
||||
} else if (interaction.isStringSelectMenu()) {
|
||||
// respond to the select menu
|
||||
}
|
||||
// [!code focus]
|
||||
},
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"pages": ["action-rows", "buttons", "select-menus", "interactions"]
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
---
|
||||
title: Select Menus
|
||||
---
|
||||
|
||||
Select menus are one of the `MessageComponent` classes, which can be sent via messages or interaction responses.
|
||||
|
||||
<Callout>
|
||||
This page is a follow-up to the [slash commands](../slash-commands/advanced-creation) section and [action
|
||||
rows](../interactive-components/action-rows) page. Please carefully read those pages first so that you can understand
|
||||
the methods used here.
|
||||
</Callout>
|
||||
|
||||
## Building string select menus
|
||||
|
||||
The "standard" and most customizable type of select menu is the string select menu. To create a string select menu, use the `StringSelectMenuBuilder` and `StringSelectMenuOptionBuilder` classes.
|
||||
|
||||
If you're a Pokémon fan, you've probably made a selection pretty similar to this example at some point in your life!
|
||||
|
||||
```js
|
||||
const { StringSelectMenuBuilder, StringSelectMenuOptionBuilder, SlashCommandBuilder } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
// data: new SlashCommandBuilder()...
|
||||
async execute(interaction) {
|
||||
const select = new StringSelectMenuBuilder()
|
||||
.setCustomId('starter')
|
||||
.setPlaceholder('Make a selection!')
|
||||
.addOptions(
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel('Bulbasaur')
|
||||
.setDescription('The dual-type Grass/Poison Seed Pokémon.')
|
||||
.setValue('bulbasaur'),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel('Charmander')
|
||||
.setDescription('The Fire-type Lizard Pokémon.')
|
||||
.setValue('charmander'),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel('Squirtle')
|
||||
.setDescription('The Water-type Tiny Turtle Pokémon.')
|
||||
.setValue('squirtle'),
|
||||
);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
<Callout>
|
||||
The custom id is a developer-defined string of up to 100 characters. Use this field to ensure you can uniquely define
|
||||
all incoming interactions from your select menus!
|
||||
</Callout>
|
||||
|
||||
## Sending select menus
|
||||
|
||||
To send your select menu, create an action row and add the buttons as components. Then, send the row in the `components` property of `InteractionReplyOptions` (extends `BaseMessageOptions`).
|
||||
|
||||
```js
|
||||
const {
|
||||
ActionRowBuilder,
|
||||
StringSelectMenuBuilder,
|
||||
StringSelectMenuOptionBuilder,
|
||||
SlashCommandBuilder,
|
||||
} = require('discord.js');
|
||||
// [!code focus:30]
|
||||
module.exports = {
|
||||
// data: new SlashCommandBuilder()...
|
||||
async execute(interaction) {
|
||||
const select = new StringSelectMenuBuilder()
|
||||
.setCustomId('starter')
|
||||
.setPlaceholder('Make a selection!')
|
||||
.addOptions(
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel('Bulbasaur')
|
||||
.setDescription('The dual-type Grass/Poison Seed Pokémon.')
|
||||
.setValue('bulbasaur'),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel('Charmander')
|
||||
.setDescription('The Fire-type Lizard Pokémon.')
|
||||
.setValue('charmander'),
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel('Squirtle')
|
||||
.setDescription('The Water-type Tiny Turtle Pokémon.')
|
||||
.setValue('squirtle'),
|
||||
);
|
||||
|
||||
// [!code ++:6]
|
||||
const row = new ActionRowBuilder().addComponents(select);
|
||||
|
||||
await interaction.reply({
|
||||
content: 'Choose your starter!',
|
||||
components: [row],
|
||||
});
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
<Callout>Remember that if you have more than one select menu, each one will need its own action row.</Callout>
|
||||
|
||||
<Callout>
|
||||
You can also send action rows inside containers or between other components, when using the [display
|
||||
components](../popular-topics/display-components) system.
|
||||
</Callout>
|
||||
|
||||
### String select menu options
|
||||
|
||||
String select menu options are custom-defined by the user, as shown in the example above. At a minimum, each option must have it's `label` and `value` defined. The label is shown to the user, while the value is included in the interaction sent to the bot.
|
||||
|
||||
In addition to these, each option can be enhanced with a `description` or `emoji`, or can be set to be selected by default.
|
||||
|
||||
```js
|
||||
const select = new StringSelectMenuBuilder().setCustomId('select').addOptions(
|
||||
// [!code focus:6]
|
||||
new StringSelectMenuOptionBuilder()
|
||||
.setLabel('Option')
|
||||
.setValue('option')
|
||||
.setDescription('A selectable option') // [!code word:setDescription]
|
||||
.setEmoji('123456789012345678') // [!code word:setEmoji]
|
||||
.setDefault(true), // [!code word:setDefault]
|
||||
);
|
||||
```
|
||||
|
||||
## Auto-populating select menus
|
||||
|
||||
Although the String select menu with its user-defined options is the most customizable, there are four other types of select menu that auto-populate with their corresponding Discord entities, much like slash command options. These are:
|
||||
|
||||
- `UserSelectMenuBuilder`
|
||||
- `RoleSelectMenuBuilder`
|
||||
- `MentionableSelectMenuBuilder`
|
||||
- `ChannelSelectMenuBuilder`
|
||||
|
||||
The `ChannelSelectMenuBuilder` can be configured to only show specific channel types using `ChannelSelectMenuBuilder#setChannelTypes`.
|
||||
|
||||
## Multi-selects
|
||||
|
||||
Where slash command options fall behind is in their single-select limitation on User, Role and Channel option types. Select menus can support this use case via `BaseSelectMenuBuilder#setMinValues` and `BaseSelectMenuBuilder#setMaxValues`. When these values are set, users can select multiple items, and the interaction will be sent with all selected values only when the user clicks outside the select menu.
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
// data: new SlashCommandBuilder()...
|
||||
async execute(interaction) {
|
||||
// [!code focus:5]
|
||||
const userSelect = new UserSelectMenuBuilder()
|
||||
.setCustomId('users')
|
||||
.setPlaceholder('Select multiple users.')
|
||||
.setMinValues(1) // [!code word:setMinValues]
|
||||
.setMaxValues(10); // [!code word:setMaxValues]
|
||||
|
||||
const row1 = new ActionRowBuilder().addComponents(userSelect);
|
||||
|
||||
await interaction.reply({
|
||||
content: 'Select users:',
|
||||
components: [row1],
|
||||
});
|
||||
},
|
||||
};
|
||||
```
|
||||
Reference in New Issue
Block a user