Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add new selects menus #1335

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file added guide/interactions/images/userselect.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 47 additions & 13 deletions guide/interactions/select-menus.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,24 @@ Select menus are one of the `MessageComponent` classes, which can be sent via me
You can have a maximum of five `ActionRow`s per message, and one select menu within an `ActionRow`.
:::

To create a select menu, use the <DocsLink path="class/ActionRowBuilder"/> and <DocsLink path="class/SelectMenuBuilder"/> classes. Then, pass the resulting row object to <DocsLink path="class/ChatInputCommandInteraction?scrollTo=reply" /> in the `components` array of <DocsLink path="typedef/InteractionReplyOptions" />:
The available select menus are: <DocsLink path="class/StringSelectMenuBuilder"/>, <DocsLink path="class/UserSelectMenuBuilder"/>, <DocsLink path="class/RoleSelectMenuBuilder"/>, <DocsLink path="class/ChannelSelectMenuBuilder"/>, <DocsLink path="class/MentionableSelectMenuBuilder"/>.
To send a select menu, use one of these with <DocsLink path="class/ActionRowBuilder"/>.
Then pass the resulting row object to <DocsLink path="class/ChatInputCommandInteraction?scrollTo=reply" /> in the `components` array of <DocsLink path="typedef/InteractionReplyOptions" />.

:::warning
The only select menu which supports options is `StringSelectMenuBuilder`.
:::

```js {1,7-24,26}
const { ActionRowBuilder, Events, SelectMenuBuilder } = require('discord.js');
const { ActionRowBuilder, Events, StringSelectMenuBuilder } = require('discord.js');

client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isChatInputCommand()) return;

if (interaction.commandName === 'ping') {
const row = new ActionRowBuilder()
.addComponents(
new SelectMenuBuilder()
new StringSelectMenuBuilder()
.setCustomId('select')
.setPlaceholder('Nothing selected')
.addOptions(
Expand Down Expand Up @@ -68,7 +74,7 @@ Restart your bot and then send the command to a channel your bot has access to.
You can also send message components within an ephemeral response or alongside message embeds.

```js {1,12-16,18}
const { ActionRowBuilder, EmbedBuilder, Events, SelectMenuBuilder } = require('discord.js');
const { ActionRowBuilder, EmbedBuilder, Events, StringSelectMenuBuilder } = require('discord.js');

client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isChatInputCommand()) return;
Expand Down Expand Up @@ -117,12 +123,24 @@ Restart your bot and then send the command to a channel your bot has access to.
-->
![selectephem](./images/selectephem.png)

## Various types

There are multiple menus like for selecting users, roles, channels and menitionables (users and roles).
```js{1}
new UserSelectMenuBuilder()
.setCustomId('user-pick')
.setLabel('Choose your favourite users')
.setMaxValues(3);
```

![userselect](./images/userselect.png)

::: warning
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 <DocsLink path="class/ActionRowBuilder"/>.

```diff
- new ActionRowBuilder()
+ new ActionRowBuilder<SelectMenuBuilder>()
+ new ActionRowBuilder<StringSelectMenuBuilder>()
```
:::

Expand All @@ -142,11 +160,11 @@ For a detailed guide on receiving message components via collectors, please refe

### The interactionCreate event

To receive a <DocsLink path="class/SelectMenuInteraction"/>, attach an <DocsLink path="class/Client?scrollTo=e-interactionCreate" /> event listener to your client and use the <DocsLink path="class/BaseInteraction?scrollTo=isSelectMenu"/> type guard to make sure you only receive select menus:
To receive a <DocsLink path="class/SelectMenuInteraction"/>, attach an <DocsLink path="class/Client?scrollTo=e-interactionCreate" /> event listener to your client and use the <DocsLink path="class/BaseInteraction?scrollTo=isAnySelectMenu"/> type guard to receive any select menus. You can use more direct typeguard like `.iStringSelectMenu()`, `.isUserSelectMenu()`, `.isRoleSelectMenu()`, `.isChannelSelectMenu()`, `.isMentionableSelectMenu()` to recive specific type of select menu:

```js {2}
client.on(Events.InteractionCreate, interaction => {
if (!interaction.isSelectMenu()) return;
if (!interaction.iStringSelectMenu()) return;
console.log(interaction);
});
```
Expand All @@ -169,7 +187,7 @@ This method should be used in favour of `editReply()` on the original interactio

```js {1,4-6}
client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isSelectMenu()) return;
if (!interaction.isStringSelectMenu()) return;

if (interaction.customId === 'select') {
await interaction.update({ content: 'Something was selected!', components: [] });
Expand All @@ -185,7 +203,7 @@ Additionally to deferring the response of the interaction, you can defer the men
const wait = require('node:timers/promises').setTimeout;

client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isSelectMenu()) return;
if (!interaction.isStringSelectMenu()) return;

if (interaction.customId === 'select') {
await interaction.deferUpdate();
Expand All @@ -194,30 +212,46 @@ client.on(Events.InteractionCreate, async interaction => {
}
});
```
## Getting options

To get options that the user seleted, you can access <DocsLink path="class/StringSelectMenuInteraction?scrollTo=values" /> which is an array of option values or access the Collection with objects through <DocsLink path="class/RoleSelectMenuInteraction?scrollTo=roles" />, <DocsLink path="class/ChannelSelectMenuInteraction?scrollTo=channels" />
```js{6}
const { Events } = require('discord.js');

client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isUserSelectMenu()) return;

// This collection can have more users if you have set max values
const user = interactions.users.first();
await interaction.reply(`Your favourite user is: ${user.tag}`);
});
```

## Multi-select menus

A select menu is not bound to only one selection; you can specify a minimum and maximum amount of options that must be selected. You can use <DocsLink path="class/SelectMenuBuilder?scrollTo=setMinValues" /> and <DocsLink path="class/SelectMenuBuilder?scrollTo=setMaxValues" /> to determine these values.
Default options can be changed by the user.

```js {1,7-31,33}
const { ActionRowBuilder, Events, SelectMenuBuilder } = require('discord.js');
```js {1,7-32,33}
const { ActionRowBuilder, Events, StringSelectMenuBuilder } = require('discord.js');

client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isChatInputCommand()) return;

if (interaction.commandName === 'ping') {
const row = new ActionRowBuilder()
.addComponents(
new SelectMenuBuilder()
new StringSelectMenuBuilder()
.setCustomId('select')
.setPlaceholder('Nothing selected')
.setMinValues(2)
.setMaxValues(3)
.addOptions([
{
label: 'Select me',
label: 'Default Option',
description: 'This is a description',
value: 'first_option',
default: true,
},
{
label: 'You can select me too',
Expand Down