Select

Step-by-Step

A simple example on the use of selects with disnake-compass.

For this example, we implement a select menu with double functionality. Firstly, the select allows you to select one of three slots. After selecting a slot, the select is modified to instead allow you to select a colour. The selected slot and colour are then combined to colour the corresponding square. and register the select.

examples/select.py - creating a select component
 1
 2LEFT = "\N{BLACK LEFT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}"
 3MIDDLE = "\N{BLACK CIRCLE FOR RECORD}\N{VARIATION SELECTOR-16}"
 4RIGHT = "\N{BLACK RIGHT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}"
 5
 6SLOT_OPTIONS = [
 7    disnake.SelectOption(label="Left", value="left", emoji=LEFT),
 8    disnake.SelectOption(label="Middle", value="middle", emoji=MIDDLE),
 9    disnake.SelectOption(label="Right", value="right", emoji=RIGHT),
10    disnake.SelectOption(label="Finalise", emoji="\N{WHITE HEAVY CHECK MARK}"),
11]
12
13
14BLACK_SQUARE = "\N{BLACK LARGE SQUARE}"
15BLUE_SQUARE = "\N{LARGE BLUE SQUARE}"
16BROWN_SQUARE = "\N{LARGE BROWN SQUARE}"
17GREEN_SQUARE = "\N{LARGE GREEN SQUARE}"
18PURPLE_SQUARE = "\N{LARGE PURPLE SQUARE}"
19RED_SQUARE = "\N{LARGE RED SQUARE}"
20WHITE_SQUARE = "\N{WHITE LARGE SQUARE}"
21YELLOW_SQUARE = "\N{LARGE YELLOW SQUARE}"
22
23COLOUR_OPTIONS = [
24    disnake.SelectOption(label="Black", value=BLACK_SQUARE, emoji=BLACK_SQUARE),
25    disnake.SelectOption(label="Blue", value=BLUE_SQUARE, emoji=BLUE_SQUARE),
26    disnake.SelectOption(label="Brown", value=BROWN_SQUARE, emoji=BROWN_SQUARE),
27    disnake.SelectOption(label="Green", value=GREEN_SQUARE, emoji=GREEN_SQUARE),
28    disnake.SelectOption(label="Purple", value=PURPLE_SQUARE, emoji=PURPLE_SQUARE),
29    disnake.SelectOption(label="Red", value=RED_SQUARE, emoji=RED_SQUARE),
30    disnake.SelectOption(label="White", value=WHITE_SQUARE, emoji=WHITE_SQUARE),
31    disnake.SelectOption(label="Yellow", value=YELLOW_SQUARE, emoji=YELLOW_SQUARE),
32]
33
34
35@manager.register
36class MySelect(disnake_compass.RichStringSelect):
37    placeholder: str | None = "Please select a square."
38    options: typing.List[disnake.SelectOption] = SLOT_OPTIONS
39
40    slot: str = "0"
41    state: str = "slot"
42    colour_left: str = BLACK_SQUARE
43    colour_middle: str = BLACK_SQUARE

First, we define our slot and colour options (lines 1-31), then inside the button we set the placeholder text and options (line 36-37).

In the custom id we store the slot the user is currently working with (line 39), whether they’re picking a slot or a colour (line 40), and we store the selected colours for the three slots (lines 41-43).

Next, we define the callback:

 1
 2    async def callback(
 3        self, interaction: disnake.MessageInteraction[disnake.Client]
 4    ) -> None:
 5        assert interaction.values is not None
 6        selected = interaction.values[0]
 7
 8        if self.state == "slot":
 9            self.handle_slots(selected)
10
11        else:
12            self.handle_colours(selected)

Tip

Since we’re dealing with a select, inter.values will never be None. Therefore, the assertion will never raise. The assertion only serves to help the type checker realise this.

If the selection was a slot, run slot selection logic (lines 5-6). To keep things tidy, we use a separate function for this. Otherwise, run colour selection logic (lines 8-9). Finally we render the new colours and update the select (lines 77-78).

Then we define handle_slots:

 1        msg = self.render_colours()
 2        component = await self.as_ui_component()
 3        await interaction.response.edit_message(msg, components=component)
 4
 5    def handle_slots(self, selected: str) -> None:
 6        if selected == "Finalise":
 7            self.disabled = True
 8            self.placeholder = "Woo!"
 9            return
10
11        self.options = COLOUR_OPTIONS

In case the user wishes to finalize, disable the select (lines 2-5). Otherwise, we update options and display (lines 7-8) and set the slot to the user’s selection and set state to colour (lines 10-11). The select will now enter colour selection mode.

Then we define handle_colours:

1
2        self.slot = selected
3        self.state = "colour"
4
5    def handle_colours(self, selected: str) -> None:

We update the options back to slot selection (line 2), set the colour attribute for the current slot (line 4), and set the state to slot (line 5). The select will now re-enter slot selection mode.

Finally we define render_colours to simply render the three colour squares:


        setattr(self, f"colour_{self.slot}", selected)

Source Code

View on GitHub: select.py

examples/select.py
  1"""A simple example on the use of selects with disnake-compass."""
  2
  3from __future__ import annotations
  4
  5import os
  6import typing
  7
  8import disnake
  9import disnake_compass
 10from disnake.ext import commands
 11
 12bot = commands.InteractionBot()
 13
 14manager = disnake_compass.get_manager()
 15manager.add_to_client(bot)
 16
 17
 18LEFT = "\N{BLACK LEFT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}"
 19MIDDLE = "\N{BLACK CIRCLE FOR RECORD}\N{VARIATION SELECTOR-16}"
 20RIGHT = "\N{BLACK RIGHT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}"
 21
 22SLOT_OPTIONS = [
 23    disnake.SelectOption(label="Left", value="left", emoji=LEFT),
 24    disnake.SelectOption(label="Middle", value="middle", emoji=MIDDLE),
 25    disnake.SelectOption(label="Right", value="right", emoji=RIGHT),
 26    disnake.SelectOption(label="Finalise", emoji="\N{WHITE HEAVY CHECK MARK}"),
 27]
 28
 29
 30BLACK_SQUARE = "\N{BLACK LARGE SQUARE}"
 31BLUE_SQUARE = "\N{LARGE BLUE SQUARE}"
 32BROWN_SQUARE = "\N{LARGE BROWN SQUARE}"
 33GREEN_SQUARE = "\N{LARGE GREEN SQUARE}"
 34PURPLE_SQUARE = "\N{LARGE PURPLE SQUARE}"
 35RED_SQUARE = "\N{LARGE RED SQUARE}"
 36WHITE_SQUARE = "\N{WHITE LARGE SQUARE}"
 37YELLOW_SQUARE = "\N{LARGE YELLOW SQUARE}"
 38
 39COLOUR_OPTIONS = [
 40    disnake.SelectOption(label="Black", value=BLACK_SQUARE, emoji=BLACK_SQUARE),
 41    disnake.SelectOption(label="Blue", value=BLUE_SQUARE, emoji=BLUE_SQUARE),
 42    disnake.SelectOption(label="Brown", value=BROWN_SQUARE, emoji=BROWN_SQUARE),
 43    disnake.SelectOption(label="Green", value=GREEN_SQUARE, emoji=GREEN_SQUARE),
 44    disnake.SelectOption(label="Purple", value=PURPLE_SQUARE, emoji=PURPLE_SQUARE),
 45    disnake.SelectOption(label="Red", value=RED_SQUARE, emoji=RED_SQUARE),
 46    disnake.SelectOption(label="White", value=WHITE_SQUARE, emoji=WHITE_SQUARE),
 47    disnake.SelectOption(label="Yellow", value=YELLOW_SQUARE, emoji=YELLOW_SQUARE),
 48]
 49
 50
 51@manager.register
 52class MySelect(disnake_compass.RichStringSelect):
 53    placeholder: str | None = "Please select a square."
 54    options: typing.List[disnake.SelectOption] = SLOT_OPTIONS
 55
 56    slot: str = "0"
 57    state: str = "slot"
 58    colour_left: str = BLACK_SQUARE
 59    colour_middle: str = BLACK_SQUARE
 60    colour_right: str = BLACK_SQUARE
 61
 62    async def callback(
 63        self, interaction: disnake.MessageInteraction[disnake.Client]
 64    ) -> None:
 65        assert interaction.values is not None
 66        selected = interaction.values[0]
 67
 68        if self.state == "slot":
 69            self.handle_slots(selected)
 70
 71        else:
 72            self.handle_colours(selected)
 73
 74        msg = self.render_colours()
 75        component = await self.as_ui_component()
 76        await interaction.response.edit_message(msg, components=component)
 77
 78    def handle_slots(self, selected: str) -> None:
 79        if selected == "Finalise":
 80            self.disabled = True
 81            self.placeholder = "Woo!"
 82            return
 83
 84        self.options = COLOUR_OPTIONS
 85        self.placeholder = f"Please select a colour for the {selected} square."
 86
 87        self.slot = selected
 88        self.state = "colour"
 89
 90    def handle_colours(self, selected: str) -> None:
 91        self.options = SLOT_OPTIONS
 92
 93        setattr(self, f"colour_{self.slot}", selected)
 94        self.state = "slot"
 95
 96    def render_colours(self) -> str:
 97        return f"{self.colour_left}{self.colour_middle}{self.colour_right}\n"
 98
 99
100@bot.slash_command()
101async def test_select(interaction: disnake.CommandInteraction[disnake.Client]) -> None:
102    my_select = MySelect()
103    await interaction.response.send_message(
104        my_select.render_colours(),
105        components=await my_select.as_ui_component(),
106    )
107
108
109bot.run(os.getenv("EXAMPLE_TOKEN"))