Skip to content

Commit 56d5f6b

Browse files
committed
docs: document modalkit
1 parent 625ae0f commit 56d5f6b

File tree

14 files changed

+301
-199
lines changed

14 files changed

+301
-199
lines changed

apps/test-bot/src/commands/misc/commandkit.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const handleButtonClick: OnButtonKitClick = async (interaction) => {
2121
});
2222
};
2323

24-
function ButtonGrid({ message }) {
24+
function ButtonGrid() {
2525
return (
2626
<>
2727
{Array.from({ length: 5 }, (_, i) => (
@@ -41,14 +41,10 @@ function ButtonGrid({ message }) {
4141
}
4242

4343
export async function run({ interaction }: SlashCommandProps) {
44-
const { resource } = await interaction.deferReply({
45-
withResponse: true,
46-
});
47-
48-
const buttons = <ButtonGrid message={resource.message} />;
44+
await interaction.deferReply();
4945

5046
await interaction.editReply({
5147
content: 'Click the button below to test CommandKit buttons.',
52-
components: buttons,
48+
components: <ButtonGrid />,
5349
});
5450
}

apps/test-bot/src/commands/misc/giveaway.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ export async function run({ interaction, client }: SlashCommandProps) {
5252

5353
const row = new ActionRowBuilder<ButtonKit>().addComponents(button);
5454

55-
const message = await interaction.reply({
55+
const {
56+
resource: { message },
57+
} = await interaction.reply({
5658
content: 'Click one of the buttons',
5759
components: [row],
58-
fetchReply: true,
60+
withResponse: true,
5961
});
6062

6163
button
@@ -68,7 +70,7 @@ export async function run({ interaction, client }: SlashCommandProps) {
6870
ephemeral: true,
6971
});
7072
},
71-
{ message, time: 10_000, autoReset: true },
73+
{ time: 10_000, autoReset: true },
7274
)
7375
.onEnd(() => {
7476
console.log('onEnd called');

apps/test-bot/src/commands/misc/run-after.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { SlashCommandProps, CommandData, after } from 'commandkit';
1+
import { SlashCommandProps, CommandData, afterCommand } from 'commandkit';
22

33
export const data: CommandData = {
44
name: 'run-after',
55
description: 'This is a run-after command',
66
};
77

88
export async function run({ interaction }: SlashCommandProps) {
9-
after((env) => {
9+
afterCommand((env) => {
1010
console.log(
1111
`The command ${interaction.commandName} was executed successfully in ${env
1212
.getExecutionTime()

apps/website/docs/guide/07-buttonkit.mdx

Lines changed: 81 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -8,233 +8,138 @@ import TabItem from '@theme/TabItem';
88

99
# Using ButtonKit
1010

11-
ButtonKit is an enhanced version of the native Discord.js [`ButtonBuilder`](https://discord.js.org/docs/packages/builders/1.9.0/ButtonBuilder:Class), designed to simplify the process of creating and handling button interactions in your Discord bot.
11+
ButtonKit extends Discord.js's [`ButtonBuilder`](https://discord.js.org/docs/packages/builders/1.9.0/ButtonBuilder:Class) to provide a simpler way to handle button interactions. It adds methods like `onClick()` to handle button clicks without manually setting up collectors.
1212

13-
It is not recommended to use this to listen for button clicks forever since it creates collectors. For that purpose, it's recommended to use a regular "interactionCreate" event listener.
13+
:::warning
14+
ButtonKit is designed for temporary button interactions. For permanent button handlers, use Discord.js's "interactionCreate" event instead.
15+
:::
1416

15-
## Handle button clicks
17+
## Basic Usage
1618

1719
<Tabs>
1820
<TabItem value="cjs" label='CommonJS' default>
19-
```js title="src/commands/counter.js"
21+
```js
2022
const { ButtonKit } = require('commandkit');
2123
const { ButtonStyle, ActionRowBuilder } = require('discord.js');
2224

23-
// Create a button
25+
// Create and configure a button
2426
const button = new ButtonKit()
25-
.setEmoji('👍')
27+
.setLabel('Click me')
2628
.setStyle(ButtonStyle.Primary)
27-
.setCustomId('button'); // Required to use onClick
28-
29-
const buttonRow = new ActionRowBuilder().addComponents(button);
30-
31-
const message = await channel.send({ components: [buttonRow] });
32-
33-
// Listen to the button interaction right away
34-
button.onClick(
35-
(interaction) => {
36-
// Reply to the interaction
37-
interaction.reply('You clicked the button!');
38-
},
39-
{ message },
40-
);
29+
.setCustomId('my-button');
30+
31+
// Add button to a row
32+
const row = new ActionRowBuilder().addComponents(button);
33+
34+
// Send message with button
35+
const message = await interaction.reply({
36+
content: 'Here is a button!',
37+
components: [row],
38+
fetchReply: true,
39+
});
40+
41+
// Handle clicks
42+
button.onClick((interaction) => {
43+
interaction.reply('Button clicked!');
44+
});
4145
```
4246

4347
</TabItem>
44-
<TabItem value='esm' label='ESM'>
45-
```js title="src/commands/counter.js"
46-
import { ButtonKit } from 'commandkit';
47-
import { ButtonStyle, ActionRowBuilder } from 'discord.js';
48-
49-
// Create a button
50-
const button = new ButtonKit()
51-
.setEmoji('👍')
52-
.setStyle(ButtonStyle.Primary)
53-
.setCustomId('button'); // Required to use onClick
54-
55-
const buttonRow = new ActionRowBuilder().addComponents(button);
56-
57-
const message = await channel.send({ components: [buttonRow] });
58-
59-
// Listen to the button interaction right away
60-
button.onClick(
61-
(interaction) => {
62-
// Reply to the interaction
63-
interaction.reply('You clicked the button!');
64-
},
65-
{ message },
66-
);
67-
```
6848

69-
</TabItem>
7049
<TabItem value='ts' label='TypeScript'>
71-
```ts title="src/commands/counter.ts"
50+
```ts
7251
import { ButtonKit } from 'commandkit';
73-
import { type ButtonInteraction, ButtonStyle, ActionRowBuilder } from 'discord.js';
52+
import {
53+
type ButtonInteraction,
54+
ButtonStyle,
55+
ActionRowBuilder
56+
} from 'discord.js';
7457

75-
// Create a button
7658
const button = new ButtonKit()
77-
.setEmoji('👍')
59+
.setLabel('Click me')
7860
.setStyle(ButtonStyle.Primary)
79-
.setCustomId('button'); // Required to use onClick
61+
.setCustomId('my-button');
8062

81-
const buttonRow = new ActionRowBuilder<ButtonKit>().addComponents(button);
63+
const row = new ActionRowBuilder<ButtonKit>().addComponents(button);
8264

83-
const message = await channel.send({ components: [buttonRow] });
65+
const message = await interaction.reply({
66+
content: 'Here is a button!',
67+
components: [row],
68+
fetchReply: true,
69+
});
8470

85-
// Listen to the button interaction right away
86-
button.onClick(
87-
(interaction: ButtonInteraction) => {
88-
// Reply to the interaction
89-
interaction.reply('You clicked the button!');
90-
},
91-
{ message },
92-
);
71+
button.onClick((interaction: ButtonInteraction) => {
72+
interaction.reply('Button clicked!');
73+
});
9374
```
9475

9576
</TabItem>
96-
9777
</Tabs>
9878

99-
In the above example, you may notice how similar `ButtonKit` is to the native Discord.js `ButtonBuilder` class. That's because it's built on top of it. It introduces a new method called `onClick` which will allow you to quickly handle button interactions without having to create collectors. CommandKit does that for you!
100-
101-
:::warning
102-
ButtonKit doesn't work without a custom ID, so ensure you provide one whenever
103-
instantiating a button. This is required to keep track of what button was
104-
clicked.
79+
:::warning Important
80+
Always set a `customId` when using ButtonKit. This is required to track button interactions.
10581
:::
10682

107-
### Arguments Explained
83+
## Configuration Options
10884

109-
Here's an empty `onClick` method without any arguments:
85+
The `onClick` method accepts two parameters:
11086

111-
```js
112-
const myButton = new ButtonKit()
113-
.setCustomId('custom_button')
114-
.setLabel('Click me!')
115-
.setStyle(ButtonStyle.Primary);
116-
117-
myButton.onClick();
118-
```
87+
1. A handler function that receives the button interaction
88+
2. An options object to configure the collector
11989

120-
The first argument required by this function is your handler function which will acknowledge button clicks (interactions) handled by ButtonKit. You can handle them like so:
90+
### Handler Function
12191

122-
```js
123-
myButton.onClick((buttonInteraction) => {
124-
buttonInteraction.reply('You clicked a button!');
125-
});
92+
```ts
93+
(interaction: ButtonInteraction) => void | Promise<void>
12694
```
12795

128-
However, the code above won't actually work since ButtonKit doesn't have any idea where it's supposed to specifically listen button clicks from. To fix that, you need to pass in a second argument, also known as your [options](/guide/buttonkit#buttonkit-onclick-options) which houses all the collector configuration. The `message` property is the message that ButtonKit will use to listen for button clicks.
129-
130-
```js
131-
const row = new ActionRowBuilder().addComponents(myButton);
132-
const message = await channel.send({ components: [row] });
96+
### Collector Options
13397

134-
myButton.onClick(
135-
(buttonInteraction) => {
136-
buttonInteraction.reply('You clicked a button!');
137-
},
138-
{ message },
139-
);
98+
```ts
99+
interface ButtonKitOptions {
100+
time?: number; // Duration in ms (default: 86400000 - 24 hours)
101+
autoReset?: boolean; // Reset timer on click (default: false)
102+
once?: boolean; // Listen for single click only (default: false)
103+
// Plus all Discord.js InteractionCollectorOptions
104+
}
140105
```
141106

142-
This also works with interaction replies. Just ensure you pass `fetchReply` alongside your components:
107+
Example with options:
143108

144109
```js
145-
const row = new ActionRowBuilder().addComponents(myButton);
146-
const message = await interaction.reply({
147-
components: [row],
148-
fetchReply: true,
149-
});
150-
151-
myButton.onClick(
152-
(buttonInteraction) => {
153-
buttonInteraction.reply('You clicked a button!');
110+
button.onClick(
111+
(interaction) => {
112+
interaction.reply('Clicked!');
113+
},
114+
{
115+
time: 60000, // 1 minute
116+
autoReset: true, // Reset timer on each click
117+
once: false, // Handle multiple clicks
154118
},
155-
{ message },
156119
);
157120
```
158121

159-
## ButtonKit `onClick` options
122+
## Handling Collector End
160123

161-
### `message`
124+
Use `onEnd()` to run code when the collector stops:
162125

163-
- Type: [`Message`](https://discord.js.org/docs/packages/discord.js/14.16.3/Message:Class)
164-
165-
The message object that ButtonKit uses to listen for button clicks (interactions).
166-
167-
### `time` (optional)
168-
169-
- Type: `number`
170-
- Default: `86400000`
171-
172-
The duration (in ms) the collector should run for and listen for button clicks.
173-
174-
### `autoReset` (optional)
175-
176-
- Type: `boolean`
177-
178-
Whether or not the collector should automatically reset the timer when a button is clicked.
179-
180-
### Additional optional options
181-
182-
- Type: [`InteractionCollectorOptions`](https://discord.js.org/docs/packages/discord.js/14.16.3/InteractionCollectorOptions:Interface)
183-
184-
## Handle collector end
185-
186-
When setting up an `onClick()` method using ButtonKit, you may also want to run some code after the collector ends running. The default timeout is 1 day, but you can modify this in [`onClickOptions#time`](#time-optional). To handle when the collector ends, you can setup an `onEnd()` method like this:
187-
188-
```js {19-24}
189-
const myButton = new ButtonKit()
190-
.setCustomId('custom_button')
191-
.setLabel('Click me!')
192-
.setStyle(ButtonStyle.Primary);
193-
194-
const row = new ActionRowBuilder().addComponents(myButton);
195-
const message = await interaction.reply({
196-
components: [row],
197-
fetchReply: true,
198-
});
199-
200-
myButton
201-
.onClick(
202-
(buttonInteraction) => {
203-
buttonInteraction.reply('You clicked a button!');
204-
},
205-
{ message },
206-
)
126+
```js
127+
button
128+
.onClick((interaction) => {
129+
interaction.reply('Clicked!');
130+
})
207131
.onEnd(() => {
208-
console.log('Button collector ended.');
209-
210-
myButton.setDisabled(true);
132+
// Disable the button
133+
button.setDisabled(true);
211134
message.edit({ components: [row] });
212135
});
213136
```
214137

215-
## Dispose button collector
216-
217-
:::warning
218-
This feature is currently only available in the [development
219-
version](/guide/installation#development-version).
220-
:::
221-
222-
To dispose the button collector, you can make use of the `dispose` method. By disposing the collector like this, your `onEnd` handler (if any) will be called automatically.
223-
224-
```js {15}
225-
myButton
226-
.onClick(
227-
(buttonInteraction) => {
228-
buttonInteraction.reply('You clicked a button!');
229-
},
230-
{ message },
231-
)
232-
.onEnd(() => {
233-
console.log('Button collector ended.');
138+
## Manually Stopping the Collector
234139

235-
myButton.setDisabled(true);
236-
message.edit({ components: [row] });
237-
});
140+
Use `dispose()` to manually stop the collector. This will trigger the `onEnd` handler if one exists.
238141

239-
myButton.dispose();
142+
```js
143+
// Stop listening for clicks
144+
button.dispose();
240145
```

0 commit comments

Comments
 (0)