|
| 1 | ++++ |
| 2 | +title = "Creating Interactive Elements" |
| 3 | +weight = 412 |
| 4 | ++++ |
| 5 | + |
| 6 | +Before you can start triggering custom commands with interactive elements---such as buttons---you'll obviously need |
| 7 | +to have elements to interact with. In this section, we'll cover how to create these elements and explore their |
| 8 | +differences. |
| 9 | + |
| 10 | +## Message Components |
| 11 | + |
| 12 | +### Buttons |
| 13 | + |
| 14 | +Buttons are probably the simplest interactive element to create, so we'll start with them. To create a button, we use |
| 15 | +the [`cbutton`](docs/reference/templates/functions#cbutton) function. In and of itself, that is rather useless, so we'll |
| 16 | +also have to attach it to a message. We do that by calling the |
| 17 | +[`complexMessage`](docs/reference/templates/functions#complexmessage) builder and adding the result of `cbutton` to it. |
| 18 | +Finally, we send the message. |
| 19 | + |
| 20 | +```yag |
| 21 | +{{ $button := cbutton "label" "My Cool Button" "custom-id" "buttons-duck" }} |
| 22 | +{{ $m := complexMessage "buttons" $button }} |
| 23 | +{{ sendMessage nil $m }} |
| 24 | +``` |
| 25 | + |
| 26 | +Result: |
| 27 | + |
| 28 | + |
| 29 | + |
| 30 | +We've successfully crated a basic button! It doesn't do anything yet, but we'll cover that in a later section. |
| 31 | +In the meantime, play around with the values `cbutton` takes. Try to attach an emoji to it, or change its style! |
| 32 | + |
| 33 | +### Select Menus |
| 34 | + |
| 35 | +Select menus act as dropdowns, allowing users to select one or more of several options. To further complicate things, |
| 36 | +select menus come in different types, each offering different functionality. We'll first focus on the most intuitive |
| 37 | +type, the **Text** select menu. This type allows you to define a custom list of options. |
| 38 | + |
| 39 | +Available select menu types are as follows: |
| 40 | + |
| 41 | +- **Text** - The options available to be selected are defined when creating the select menu. Options have labels, and |
| 42 | + can optionally have emojis and longer-form descriptions. |
| 43 | +- **User** - The options are populated with users on the server by Discord. |
| 44 | +- **Role** - The options are populated with roles on the server by Discord. |
| 45 | +- **Mentionable** - The options are auto-populated with both users and roles on the server by Discord, allowing members |
| 46 | + to select both. |
| 47 | +- **Channel** - The options are populated with channels on the server by Discord. You can limit which channel types |
| 48 | + appear as options when creating the select menu. |
| 49 | + |
| 50 | +#### Text Select Menus |
| 51 | + |
| 52 | +To create a text select menu, we use the [`cmenu`](docs/reference/templates/functions#cmenu) function. Then, just like |
| 53 | +with a button, we attach it to a message and send it. |
| 54 | + |
| 55 | +```yag |
| 56 | +{{ $menu := cmenu |
| 57 | + "type" "text" |
| 58 | + "placeholder" "Choose a terrible thing" |
| 59 | + "custom_id" "menus-duck" |
| 60 | + "options" (cslice |
| 61 | + (sdict "label" "Two Ducks" "value" "opt-1" "default" true) |
| 62 | + (sdict "label" "A Duck" "value" "duck-option" "emoji" (sdict "name" "🦆")) |
| 63 | + (sdict "label" "Half a Duck" "value" "third-option" "description" "Don't let the smaller amount fool you.")) |
| 64 | + "max_values" 3 |
| 65 | +}} |
| 66 | +
|
| 67 | +{{ sendMessage nil (complexMessage "menus" $menu) }} |
| 68 | +``` |
| 69 | + |
| 70 | +Opening the select menu that was sent using the above code should yield the following result: |
| 71 | + |
| 72 | + |
| 73 | + |
| 74 | +In this menu, our first option (Ducks) is defined as `default`, which is why it is already selected when we look at the |
| 75 | +menu on our server. You can define multiple default options, however the amount of default options you define must fall |
| 76 | +between your `min_values` and `max_values`. |
| 77 | + |
| 78 | +We have also set the `max_values` to 3, and we haven't set a `min_values` argument. This means the server member could |
| 79 | +select anywhere between 1 and 3 of these options. |
| 80 | + |
| 81 | +#### Other Select Menu Types |
| 82 | + |
| 83 | +The other select menu types are created in the same way as the text select menu, but with a few differences. As Discord |
| 84 | +automatically populates the options, you need not---nor can you---define these options. |
| 85 | + |
| 86 | +```yag |
| 87 | +{{ $menu := cmenu |
| 88 | + "type" "role" |
| 89 | + "placeholder" "Choose roles who are secretly ducks" |
| 90 | + "custom_id" "menus-duck-roles" |
| 91 | + "max_values" 3 |
| 92 | +}} |
| 93 | +
|
| 94 | +{{ sendMessage nil (complexMessage "menus" $menu) }} |
| 95 | +``` |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | +## Modals |
| 100 | + |
| 101 | +Modals are a pop-up form that YAGPDB can send in response to an interaction. It allows users to privately input text |
| 102 | +which is sent directly to YAGPDB for use in your custom command. Although modals are not interactive elements in the |
| 103 | +same way buttons and menus are, we will cover them here for completeness' sake. |
| 104 | + |
| 105 | +Modals are created using the [`cmodal`](docs/reference/templates/functions#cmodal) function. The modal is then sent |
| 106 | +with the [`sendModal`](docs/reference/templates/functions#sendmodal) function. Sending a modal is strictly a response, |
| 107 | +meaning it can only be sent once a user clicks a button or uses a select menu. You cannot send a modal as a response to |
| 108 | +a user submitting a modal. |
| 109 | + |
| 110 | +### Modal Structure |
| 111 | + |
| 112 | +| Field | Description | |
| 113 | +| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
| 114 | +| Title | The modal's title, appears at the top of the modal while a user is filling it out. | |
| 115 | +| Custom ID | The Custom ID is referenced to trigger a custom command when the modal is submitted (which you'll need to do if you care about retrieving what the user inputted). | |
| 116 | +| Fields | A slice of [discordgo.TextInputComponent](https://discord.com/developers/docs/components/reference#text-input) objects. | |
| 117 | + |
| 118 | +Let's consider the following piece of code: |
| 119 | + |
| 120 | +```yag |
| 121 | +{{ $modal := cmodal |
| 122 | + "title" "My Custom Modal" |
| 123 | + "custom_id" "modals-my_first_modal" |
| 124 | + "fields" (cslice |
| 125 | + (sdict "label" "Name" "placeholder" "Duck" "required" true) |
| 126 | + (sdict "label" "Do you like ducks?" "value" "Heck no") |
| 127 | + (sdict "label" "Duck hate essay" "min_length" 100 "style" 2)) }} |
| 128 | +{{ sendModal $modal }} |
| 129 | +``` |
| 130 | + |
| 131 | +This code will send a modal with three fields. The first field is a required text input, with "Duck" as a placeholder, |
| 132 | +the second field is a text input with a default value, and the third field is a long-form text input that requires at |
| 133 | +least 100 characters. The custom ID is set to `modals-my_first_modal`, which helps us identifying the modal when we use |
| 134 | +a modal submission trigger for a custom command. |
| 135 | + |
| 136 | +## Multiple Components |
| 137 | + |
| 138 | +You learnt how to create messages with just one component attached to it, but the whole point of components is to have |
| 139 | +*a lot* of them available. Let's start with adding some more buttons! |
| 140 | + |
| 141 | +### More Buttons |
| 142 | + |
| 143 | +{{< callout context="note" title="Note: Button Type Limitations" icon="outline/info-circle" >}} |
| 144 | + |
| 145 | +Buttons with the "link" style cannot have a Custom ID, and instead require a URL field. |
| 146 | + |
| 147 | +Link style buttons do not trigger interactions. |
| 148 | + |
| 149 | +{{< /callout >}} |
| 150 | + |
| 151 | +To add more buttons to a single row, simply toss them all into a slice, like so. |
| 152 | + |
| 153 | +```yag |
| 154 | +{{ $button1 := cbutton "label" "Duck One" "custom_id" "buttons-duck-alpha" "style" "success" }} |
| 155 | +{{ $button2 := cbutton "emoji" (sdict "name" "🦆") "custom_id" "buttons-duck-beta" "style" "danger" }} |
| 156 | +{{ $button3 := cbutton "label" "Duck Three" "emoji" (sdict "name" "🦆") "url" "https://yagpdb.xyz" "style" "link" }} |
| 157 | +{{ $message := complexMessage "buttons" (cslice $button1 $button2 $button3) }} |
| 158 | +{{ sendMessage nil $message }} |
| 159 | +``` |
| 160 | + |
| 161 | + |
| 162 | + |
| 163 | +At this stage we have three buttons. Both of the first two buttons will trigger our duck trigger custom command, but the |
| 164 | +third button will not trigger any custom command. Link buttons do not create _interactions_. |
| 165 | + |
| 166 | +We can differentiate between the two buttons using `.StrippedID`, which, just like `.StrippedMsg`, returns our Custom ID |
| 167 | +without the trigger and everything else before that. In our example, `.StrippedID` will return `-alpha` for the first |
| 168 | +button and `-beta` for the second button. |
| 169 | + |
| 170 | +Confirming this behavior will be left as an exercise to the reader (you). |
| 171 | + |
| 172 | +Let's add a select menu as well, now. |
| 173 | + |
| 174 | +### Buttons and a Select Menu |
| 175 | + |
| 176 | +We start by copying the previous code over, and define our menu using `cmenu`. Then, it's just simply adding our newly |
| 177 | +created menu to the `"menu"` key of the complex message builder, and sending said message. |
| 178 | + |
| 179 | +```yag |
| 180 | +{{ $button1 := cbutton "label" "Duck One" "custom_id" "buttons-duck-alpha" "style" "success" }} |
| 181 | +{{ $button2 := cbutton "emoji" (sdict "name" "🦆") "custom_id" "buttons-duck-beta" "style" "danger" }} |
| 182 | +{{ $button3 := cbutton "label" "Duck Three" "emoji" (sdict "name" "🦆") "url" "https://yagpdb.xyz" "style" "link" }} |
| 183 | +
|
| 184 | +{{ $menu := cmenu |
| 185 | + "type" "text" |
| 186 | + "placeholder" "Choose a terrible thing" |
| 187 | + "custom_id" "menus-duck-alpha" |
| 188 | + "options" (cslice |
| 189 | + (sdict "label" "Ducks" "value" "opt-1" "default" true) |
| 190 | + (sdict "label" "Duck" "value" "opt-2" "emoji" (sdict "name" "🦆")) |
| 191 | + (sdict "label" "Half a Duck" "value" "opt-3" "description" "Don't let the smaller amount fool you.")) |
| 192 | + "max_values" 3 }} |
| 193 | +
|
| 194 | +{{ $message := complexMessage "buttons" (cslice $button1 $button2 $button3) "menus" $menu }} |
| 195 | +{{ sendMessage nil $message }} |
| 196 | +``` |
| 197 | + |
| 198 | + |
| 199 | + |
| 200 | +### Ordering Components |
| 201 | + |
| 202 | +Let's say we want to play Tic Tac Toe. If we just add nine buttons into the same slice in our complex message builder, |
| 203 | +they will just fill the first row with five buttons, and the second row with four buttons, which is definitely not what |
| 204 | +we are looking for. The solution is to tell YAGPDB precisely how the rows look like and then pass each row to the |
| 205 | +`"button"` or `"menu"` key. |
| 206 | + |
| 207 | +```yag |
| 208 | +{{ $blankEmoji := sdict "name" "⬜" }} |
| 209 | +
|
| 210 | +{{ $row1 := cslice (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-1" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-2" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-3" "style" "secondary") }} |
| 211 | +{{ $row2 := cslice (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-4" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-5" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-6" "style" "secondary") }} |
| 212 | +{{ $row3 := cslice (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-7" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-8" "style" "secondary") (cbutton "emoji" $blankEmoji "custom_id" "tictactoe-button-9" "style" "secondary") }} |
| 213 | +
|
| 214 | +{{ $message := complexMessage "buttons" $row1 "buttons" $row2 "buttons" $row3 "menus" }} |
| 215 | +{{ sendMessage nil $message }} |
| 216 | +``` |
| 217 | + |
| 218 | + |
| 219 | + |
| 220 | +#### Action Rows |
| 221 | + |
| 222 | +As you continue to attach more components, YAGPDB will automatically overflow into new rows. However, this may be |
| 223 | +undesirable in some cases, such as when you want to have a specific layout for your buttons and/or menus. For that |
| 224 | +purpose, `complexMessage` accepts another key, `"components"`, which is essentially just you providing all the action |
| 225 | +rows you want. |
| 226 | + |
| 227 | +To illustrate, we'll create a message with five action rows, each with a different number of buttons or menus in it. |
| 228 | + |
| 229 | +```yag |
| 230 | +{{ $row1 := cslice (cbutton "label" "Row 1 - Button 1") (cbutton "label" "Row 1 - Button 2") (cbutton "label" "Row 1 - Button 3") (cbutton "label" "Row 1 - Button 4") }} |
| 231 | +{{ $row2 := cslice (cbutton "label" "Row 2 - Button 1") (cbutton "label" "Row 2 - Button 1") }} |
| 232 | +{{ $row3 := cslice (cmenu "type" "mentionable") }} |
| 233 | +{{ $row4 := cslice (cbutton "label" "Row 3 - Button 1") (cbutton "label" "Row 3 - Button 2") (cbutton "label" "Row 3 - Button 3") }} |
| 234 | +{{ $row5 := cslice (cmenu "type" "channel") }} |
| 235 | +
|
| 236 | +{{ $rows := cslice $row1 $row2 $row3 $row4 $row5 }} |
| 237 | +
|
| 238 | +{{ $message := complexMessage "components" $rows }} |
| 239 | +{{ sendMessage nil $message }} |
| 240 | +``` |
| 241 | + |
| 242 | + |
| 243 | + |
| 244 | +## Final Notes |
| 245 | + |
| 246 | +### Using emojis |
| 247 | + |
| 248 | +Buttons and Select Menu Options both have an `"emoji"` field, but this field does not accept the regular unicode/name:id |
| 249 | +formula like reactions do. Emojis in components follow the [partial emoji |
| 250 | +object](https://discord.com/developers/docs/resources/emoji#emoji-object) structure, however only the ID *or* the Name |
| 251 | +fields are required, depending on if you are using a custom emoji or not. |
| 252 | + |
| 253 | +| Field | Description | |
| 254 | +| ----- | ---------------------------------------------------------------------------------------------------- | |
| 255 | +| ID | ID of the emoji, only necessary when using Custom Emoji. | |
| 256 | +| Name | Name of the emoji, use the unicode character here. Only necessary when using builtin unicode emojis. | |
| 257 | + |
| 258 | +```yag |
| 259 | +{{ $unicodeEmojiButton := cbutton "emoji" (sdict "name" "😀") }} |
| 260 | +{{ $customEmojiButton := cbutton "emoji" (sdict "id" "733037741532643428") }} |
| 261 | +{{ $animatedEmojiButton := cbutton "emoji" (sdict "id" "786307104247775302") }} |
| 262 | +
|
| 263 | +{{ $components := cslice $unicodeEmojiButton $customEmojiButton $animatedEmojiButton }} |
| 264 | +{{ sendMessage nil (complexMessage "components" $components)}} |
| 265 | +``` |
| 266 | + |
| 267 | +### Using dictionaries instead |
| 268 | + |
| 269 | +As you probably already discovered when building embeds, you can use `sdict` in favor of `cembed` to create some sort of |
| 270 | +skeleton of your embed for later adjustments, with YAGPDB's template engine automagically handling the conversion for |
| 271 | +you. This is also true for all the elements we covered in this section, which makes it far easier to conditionally |
| 272 | +change certain aspects of your interactive elements. |
0 commit comments