mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-19 21:13:30 +01:00
feat: allow builders to accept rest params and arrays (#7874)
Co-authored-by: Parbez <imranbarbhuiya.fsd@gmail.com> Co-authored-by: Khafra <42794878+KhafraDev@users.noreply.github.com>
This commit is contained in:
@@ -45,6 +45,8 @@ const rowWithSelectMenuData: APIActionRowComponent<APIMessageActionRowComponent>
|
|||||||
describe('Action Row Components', () => {
|
describe('Action Row Components', () => {
|
||||||
describe('Assertion Tests', () => {
|
describe('Assertion Tests', () => {
|
||||||
test('GIVEN valid components THEN do not throw', () => {
|
test('GIVEN valid components THEN do not throw', () => {
|
||||||
|
expect(() => new ActionRowBuilder().addComponents(new ButtonBuilder())).not.toThrowError();
|
||||||
|
expect(() => new ActionRowBuilder().setComponents(new ButtonBuilder())).not.toThrowError();
|
||||||
expect(() => new ActionRowBuilder().addComponents([new ButtonBuilder()])).not.toThrowError();
|
expect(() => new ActionRowBuilder().addComponents([new ButtonBuilder()])).not.toThrowError();
|
||||||
expect(() => new ActionRowBuilder().setComponents([new ButtonBuilder()])).not.toThrowError();
|
expect(() => new ActionRowBuilder().setComponents([new ButtonBuilder()])).not.toThrowError();
|
||||||
});
|
});
|
||||||
@@ -131,11 +133,17 @@ describe('Action Row Components', () => {
|
|||||||
.setCustomId('1234')
|
.setCustomId('1234')
|
||||||
.setMaxValues(10)
|
.setMaxValues(10)
|
||||||
.setMinValues(12)
|
.setMinValues(12)
|
||||||
|
.setOptions(
|
||||||
|
new SelectMenuOptionBuilder().setLabel('one').setValue('one'),
|
||||||
|
new SelectMenuOptionBuilder().setLabel('two').setValue('two'),
|
||||||
|
)
|
||||||
.setOptions([
|
.setOptions([
|
||||||
new SelectMenuOptionBuilder().setLabel('one').setValue('one'),
|
new SelectMenuOptionBuilder().setLabel('one').setValue('one'),
|
||||||
new SelectMenuOptionBuilder().setLabel('two').setValue('two'),
|
new SelectMenuOptionBuilder().setLabel('two').setValue('two'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
expect(new ActionRowBuilder().addComponents(button).toJSON()).toEqual(rowWithButtonData);
|
||||||
|
expect(new ActionRowBuilder().addComponents(selectMenu).toJSON()).toEqual(rowWithSelectMenuData);
|
||||||
expect(new ActionRowBuilder().addComponents([button]).toJSON()).toEqual(rowWithButtonData);
|
expect(new ActionRowBuilder().addComponents([button]).toJSON()).toEqual(rowWithButtonData);
|
||||||
expect(new ActionRowBuilder().addComponents([selectMenu]).toJSON()).toEqual(rowWithSelectMenuData);
|
expect(new ActionRowBuilder().addComponents([selectMenu]).toJSON()).toEqual(rowWithSelectMenuData);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -44,12 +44,15 @@ describe('Select Menu Components', () => {
|
|||||||
.setDefault(true)
|
.setDefault(true)
|
||||||
.setEmoji({ name: 'test' })
|
.setEmoji({ name: 'test' })
|
||||||
.setDescription('description');
|
.setDescription('description');
|
||||||
|
expect(() => selectMenu().addOptions(option)).not.toThrowError();
|
||||||
|
expect(() => selectMenu().setOptions(option)).not.toThrowError();
|
||||||
|
expect(() => selectMenu().setOptions({ label: 'test', value: 'test' })).not.toThrowError();
|
||||||
expect(() => selectMenu().addOptions([option])).not.toThrowError();
|
expect(() => selectMenu().addOptions([option])).not.toThrowError();
|
||||||
expect(() => selectMenu().setOptions([option])).not.toThrowError();
|
expect(() => selectMenu().setOptions([option])).not.toThrowError();
|
||||||
expect(() => selectMenu().setOptions([{ label: 'test', value: 'test' }])).not.toThrowError();
|
expect(() => selectMenu().setOptions([{ label: 'test', value: 'test' }])).not.toThrowError();
|
||||||
expect(() =>
|
expect(() =>
|
||||||
selectMenu().addOptions([
|
selectMenu()
|
||||||
{
|
.addOptions({
|
||||||
label: 'test',
|
label: 'test',
|
||||||
value: 'test',
|
value: 'test',
|
||||||
emoji: {
|
emoji: {
|
||||||
@@ -57,14 +60,31 @@ describe('Select Menu Components', () => {
|
|||||||
name: 'test',
|
name: 'test',
|
||||||
animated: true,
|
animated: true,
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
]),
|
.addOptions([
|
||||||
|
{
|
||||||
|
label: 'test',
|
||||||
|
value: 'test',
|
||||||
|
emoji: {
|
||||||
|
id: '123',
|
||||||
|
name: 'test',
|
||||||
|
animated: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]),
|
||||||
).not.toThrowError();
|
).not.toThrowError();
|
||||||
|
|
||||||
const options = new Array<APISelectMenuOption>(25).fill({ label: 'test', value: 'test' });
|
const options = new Array<APISelectMenuOption>(25).fill({ label: 'test', value: 'test' });
|
||||||
|
expect(() => selectMenu().addOptions(...options)).not.toThrowError();
|
||||||
|
expect(() => selectMenu().setOptions(...options)).not.toThrowError();
|
||||||
expect(() => selectMenu().addOptions(options)).not.toThrowError();
|
expect(() => selectMenu().addOptions(options)).not.toThrowError();
|
||||||
expect(() => selectMenu().setOptions(options)).not.toThrowError();
|
expect(() => selectMenu().setOptions(options)).not.toThrowError();
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
selectMenu()
|
||||||
|
.addOptions({ label: 'test', value: 'test' })
|
||||||
|
.addOptions(...new Array<APISelectMenuOption>(24).fill({ label: 'test', value: 'test' })),
|
||||||
|
).not.toThrowError();
|
||||||
expect(() =>
|
expect(() =>
|
||||||
selectMenu()
|
selectMenu()
|
||||||
.addOptions([{ label: 'test', value: 'test' }])
|
.addOptions([{ label: 'test', value: 'test' }])
|
||||||
@@ -80,6 +100,17 @@ describe('Select Menu Components', () => {
|
|||||||
expect(() => selectMenu().setDisabled(0)).toThrowError();
|
expect(() => selectMenu().setDisabled(0)).toThrowError();
|
||||||
expect(() => selectMenu().setPlaceholder(longStr)).toThrowError();
|
expect(() => selectMenu().setPlaceholder(longStr)).toThrowError();
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
|
expect(() => selectMenu().addOptions({ label: 'test' })).toThrowError();
|
||||||
|
expect(() => selectMenu().addOptions({ label: longStr, value: 'test' })).toThrowError();
|
||||||
|
expect(() => selectMenu().addOptions({ value: longStr, label: 'test' })).toThrowError();
|
||||||
|
expect(() => selectMenu().addOptions({ label: 'test', value: 'test', description: longStr })).toThrowError();
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(() => selectMenu().addOptions({ label: 'test', value: 'test', default: 100 })).toThrowError();
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(() => selectMenu().addOptions({ value: 'test' })).toThrowError();
|
||||||
|
// @ts-expect-error
|
||||||
|
expect(() => selectMenu().addOptions({ default: true })).toThrowError();
|
||||||
|
// @ts-expect-error
|
||||||
expect(() => selectMenu().addOptions([{ label: 'test' }])).toThrowError();
|
expect(() => selectMenu().addOptions([{ label: 'test' }])).toThrowError();
|
||||||
expect(() => selectMenu().addOptions([{ label: longStr, value: 'test' }])).toThrowError();
|
expect(() => selectMenu().addOptions([{ label: longStr, value: 'test' }])).toThrowError();
|
||||||
expect(() => selectMenu().addOptions([{ value: longStr, label: 'test' }])).toThrowError();
|
expect(() => selectMenu().addOptions([{ value: longStr, label: 'test' }])).toThrowError();
|
||||||
@@ -92,8 +123,14 @@ describe('Select Menu Components', () => {
|
|||||||
expect(() => selectMenu().addOptions([{ default: true }])).toThrowError();
|
expect(() => selectMenu().addOptions([{ default: true }])).toThrowError();
|
||||||
|
|
||||||
const tooManyOptions = new Array<APISelectMenuOption>(26).fill({ label: 'test', value: 'test' });
|
const tooManyOptions = new Array<APISelectMenuOption>(26).fill({ label: 'test', value: 'test' });
|
||||||
|
expect(() => selectMenu().setOptions(...tooManyOptions)).toThrowError();
|
||||||
expect(() => selectMenu().setOptions(tooManyOptions)).toThrowError();
|
expect(() => selectMenu().setOptions(tooManyOptions)).toThrowError();
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
selectMenu()
|
||||||
|
.addOptions({ label: 'test', value: 'test' })
|
||||||
|
.addOptions(...tooManyOptions),
|
||||||
|
).toThrowError();
|
||||||
expect(() =>
|
expect(() =>
|
||||||
selectMenu()
|
selectMenu()
|
||||||
.addOptions([{ label: 'test', value: 'test' }])
|
.addOptions([{ label: 'test', value: 'test' }])
|
||||||
@@ -113,6 +150,11 @@ describe('Select Menu Components', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('GIVEN valid JSON input THEN valid JSON history is correct', () => {
|
test('GIVEN valid JSON input THEN valid JSON history is correct', () => {
|
||||||
|
expect(
|
||||||
|
new SelectMenuBuilder(selectMenuDataWithoutOptions)
|
||||||
|
.addOptions(new SelectMenuOptionBuilder(selectMenuOptionData))
|
||||||
|
.toJSON(),
|
||||||
|
).toEqual(selectMenuData);
|
||||||
expect(
|
expect(
|
||||||
new SelectMenuBuilder(selectMenuDataWithoutOptions)
|
new SelectMenuBuilder(selectMenuDataWithoutOptions)
|
||||||
.addOptions([new SelectMenuOptionBuilder(selectMenuOptionData)])
|
.addOptions([new SelectMenuOptionBuilder(selectMenuOptionData)])
|
||||||
|
|||||||
@@ -49,7 +49,11 @@ describe('Modals', () => {
|
|||||||
|
|
||||||
test('GIVEN valid fields THEN builder does not throw', () => {
|
test('GIVEN valid fields THEN builder does not throw', () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
modal().setTitle('test').setCustomId('foobar').setComponents([new ActionRowBuilder()]),
|
modal()
|
||||||
|
.setTitle('test')
|
||||||
|
.setCustomId('foobar')
|
||||||
|
.setComponents(new ActionRowBuilder())
|
||||||
|
.addComponents([new ActionRowBuilder()]),
|
||||||
).not.toThrowError();
|
).not.toThrowError();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -75,6 +79,17 @@ describe('Modals', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: ComponentType.ActionRow,
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: ComponentType.TextInput,
|
||||||
|
label: 'label',
|
||||||
|
style: TextInputStyle.Paragraph,
|
||||||
|
custom_id: 'custom id',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,10 +99,15 @@ describe('Modals', () => {
|
|||||||
modal()
|
modal()
|
||||||
.setTitle(modalData.title)
|
.setTitle(modalData.title)
|
||||||
.setCustomId('custom id')
|
.setCustomId('custom id')
|
||||||
.setComponents([
|
.setComponents(
|
||||||
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents([
|
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
|
||||||
new TextInputBuilder().setCustomId('custom id').setLabel('label').setStyle(TextInputStyle.Paragraph),
|
new TextInputBuilder().setCustomId('custom id').setLabel('label').setStyle(TextInputStyle.Paragraph),
|
||||||
]),
|
),
|
||||||
|
)
|
||||||
|
.addComponents([
|
||||||
|
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
|
||||||
|
new TextInputBuilder().setCustomId('custom id').setLabel('label').setStyle(TextInputStyle.Paragraph),
|
||||||
|
),
|
||||||
])
|
])
|
||||||
.toJSON(),
|
.toJSON(),
|
||||||
).toEqual(modalData);
|
).toEqual(modalData);
|
||||||
|
|||||||
@@ -323,19 +323,20 @@ describe('Embed', () => {
|
|||||||
|
|
||||||
test('GIVEN an embed using Embed#addFields THEN returns valid toJSON data', () => {
|
test('GIVEN an embed using Embed#addFields THEN returns valid toJSON data', () => {
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
|
embed.addFields({ name: 'foo', value: 'bar' });
|
||||||
embed.addFields([{ name: 'foo', value: 'bar' }]);
|
embed.addFields([{ name: 'foo', value: 'bar' }]);
|
||||||
|
|
||||||
expect(embed.toJSON()).toStrictEqual({
|
expect(embed.toJSON()).toStrictEqual({
|
||||||
fields: [{ name: 'foo', value: 'bar' }],
|
fields: [
|
||||||
|
{ name: 'foo', value: 'bar' },
|
||||||
|
{ name: 'foo', value: 'bar' },
|
||||||
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('GIVEN an embed using Embed#spliceFields THEN returns valid toJSON data', () => {
|
test('GIVEN an embed using Embed#spliceFields THEN returns valid toJSON data', () => {
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
embed.addFields([
|
embed.addFields({ name: 'foo', value: 'bar' }, { name: 'foo', value: 'baz' });
|
||||||
{ name: 'foo', value: 'bar' },
|
|
||||||
{ name: 'foo', value: 'baz' },
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(embed.spliceFields(0, 1).toJSON()).toStrictEqual({
|
expect(embed.spliceFields(0, 1).toJSON()).toStrictEqual({
|
||||||
fields: [{ name: 'foo', value: 'baz' }],
|
fields: [{ name: 'foo', value: 'baz' }],
|
||||||
@@ -344,7 +345,7 @@ describe('Embed', () => {
|
|||||||
|
|
||||||
test('GIVEN an embed using Embed#spliceFields THEN returns valid toJSON data 2', () => {
|
test('GIVEN an embed using Embed#spliceFields THEN returns valid toJSON data 2', () => {
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
embed.addFields(Array.from({ length: 23 }, () => ({ name: 'foo', value: 'bar' })));
|
embed.addFields(...Array.from({ length: 23 }, () => ({ name: 'foo', value: 'bar' })));
|
||||||
|
|
||||||
expect(() =>
|
expect(() =>
|
||||||
embed.spliceFields(0, 3, ...Array.from({ length: 5 }, () => ({ name: 'foo', value: 'bar' }))),
|
embed.spliceFields(0, 3, ...Array.from({ length: 5 }, () => ({ name: 'foo', value: 'bar' }))),
|
||||||
@@ -353,7 +354,7 @@ describe('Embed', () => {
|
|||||||
|
|
||||||
test('GIVEN an embed using Embed#spliceFields that adds additional fields resulting in fields > 25 THEN throws error', () => {
|
test('GIVEN an embed using Embed#spliceFields that adds additional fields resulting in fields > 25 THEN throws error', () => {
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
embed.addFields(Array.from({ length: 23 }, () => ({ name: 'foo', value: 'bar' })));
|
embed.addFields(...Array.from({ length: 23 }, () => ({ name: 'foo', value: 'bar' })));
|
||||||
|
|
||||||
expect(() =>
|
expect(() =>
|
||||||
embed.spliceFields(0, 3, ...Array.from({ length: 8 }, () => ({ name: 'foo', value: 'bar' }))),
|
embed.spliceFields(0, 3, ...Array.from({ length: 8 }, () => ({ name: 'foo', value: 'bar' }))),
|
||||||
@@ -363,6 +364,9 @@ describe('Embed', () => {
|
|||||||
test('GIVEN an embed using Embed#setFields THEN returns valid toJSON data', () => {
|
test('GIVEN an embed using Embed#setFields THEN returns valid toJSON data', () => {
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
embed.setFields(...Array.from({ length: 25 }, () => ({ name: 'foo', value: 'bar' }))),
|
||||||
|
).not.toThrowError();
|
||||||
expect(() =>
|
expect(() =>
|
||||||
embed.setFields(Array.from({ length: 25 }, () => ({ name: 'foo', value: 'bar' }))),
|
embed.setFields(Array.from({ length: 25 }, () => ({ name: 'foo', value: 'bar' }))),
|
||||||
).not.toThrowError();
|
).not.toThrowError();
|
||||||
@@ -371,6 +375,9 @@ describe('Embed', () => {
|
|||||||
test('GIVEN an embed using Embed#setFields that sets more than 25 fields THEN throws error', () => {
|
test('GIVEN an embed using Embed#setFields that sets more than 25 fields THEN throws error', () => {
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
embed.setFields(...Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' }))),
|
||||||
|
).toThrowError();
|
||||||
expect(() => embed.setFields(Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' })))).toThrowError();
|
expect(() => embed.setFields(Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' })))).toThrowError();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -378,7 +385,9 @@ describe('Embed', () => {
|
|||||||
test('1', () => {
|
test('1', () => {
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
|
|
||||||
expect(() => embed.addFields(Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' })))).toThrowError();
|
expect(() =>
|
||||||
|
embed.addFields(...Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' }))),
|
||||||
|
).toThrowError();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -386,7 +395,7 @@ describe('Embed', () => {
|
|||||||
test('2', () => {
|
test('2', () => {
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
|
|
||||||
expect(() => embed.addFields([{ name: '', value: 'bar' }])).toThrowError();
|
expect(() => embed.addFields({ name: '', value: 'bar' })).toThrowError();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -394,7 +403,7 @@ describe('Embed', () => {
|
|||||||
test('3', () => {
|
test('3', () => {
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
|
|
||||||
expect(() => embed.addFields([{ name: 'a'.repeat(257), value: 'bar' }])).toThrowError();
|
expect(() => embed.addFields({ name: 'a'.repeat(257), value: 'bar' })).toThrowError();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -402,7 +411,7 @@ describe('Embed', () => {
|
|||||||
test('4', () => {
|
test('4', () => {
|
||||||
const embed = new EmbedBuilder();
|
const embed = new EmbedBuilder();
|
||||||
|
|
||||||
expect(() => embed.addFields([{ name: '', value: 'a'.repeat(1025) }])).toThrowError();
|
expect(() => embed.addFields({ name: '', value: 'a'.repeat(1025) })).toThrowError();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import { ComponentBuilder } from './Component';
|
import { ComponentBuilder } from './Component';
|
||||||
import { createComponentBuilder } from './Components';
|
import { createComponentBuilder } from './Components';
|
||||||
import type { ButtonBuilder, SelectMenuBuilder, TextInputBuilder } from '..';
|
import type { ButtonBuilder, SelectMenuBuilder, TextInputBuilder } from '..';
|
||||||
|
import { normalizeArray, type RestOrArray } from '../util/normalizeArray';
|
||||||
|
|
||||||
export type MessageComponentBuilder =
|
export type MessageComponentBuilder =
|
||||||
| MessageActionRowComponentBuilder
|
| MessageActionRowComponentBuilder
|
||||||
@@ -38,8 +39,8 @@ export class ActionRowBuilder<T extends AnyComponentBuilder> extends ComponentBu
|
|||||||
* @param components The components to add to this action row.
|
* @param components The components to add to this action row.
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public addComponents(components: T[]) {
|
public addComponents(...components: RestOrArray<T>) {
|
||||||
this.components.push(...components);
|
this.components.push(...normalizeArray(components));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +48,8 @@ export class ActionRowBuilder<T extends AnyComponentBuilder> extends ComponentBu
|
|||||||
* Sets the components in this action row
|
* Sets the components in this action row
|
||||||
* @param components The components to set this row to
|
* @param components The components to set this row to
|
||||||
*/
|
*/
|
||||||
public setComponents(components: T[]) {
|
public setComponents(...components: RestOrArray<T>) {
|
||||||
this.components.splice(0, this.components.length, ...components);
|
this.components.splice(0, this.components.length, ...normalizeArray(components));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { APISelectMenuComponent, APISelectMenuOption } from 'discord-api-types/v10';
|
import type { APISelectMenuComponent, APISelectMenuOption } from 'discord-api-types/v10';
|
||||||
import { UnsafeSelectMenuBuilder } from './UnsafeSelectMenu';
|
import { UnsafeSelectMenuBuilder } from './UnsafeSelectMenu';
|
||||||
import { UnsafeSelectMenuOptionBuilder } from './UnsafeSelectMenuOption';
|
import { UnsafeSelectMenuOptionBuilder } from './UnsafeSelectMenuOption';
|
||||||
|
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
||||||
import {
|
import {
|
||||||
customIdValidator,
|
customIdValidator,
|
||||||
disabledValidator,
|
disabledValidator,
|
||||||
@@ -35,7 +36,8 @@ export class SelectMenuBuilder extends UnsafeSelectMenuBuilder {
|
|||||||
return super.setDisabled(disabledValidator.parse(disabled));
|
return super.setDisabled(disabledValidator.parse(disabled));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override addOptions(options: (UnsafeSelectMenuOptionBuilder | APISelectMenuOption)[]) {
|
public override addOptions(...options: RestOrArray<UnsafeSelectMenuOptionBuilder | APISelectMenuOption>) {
|
||||||
|
options = normalizeArray(options);
|
||||||
optionsLengthValidator.parse(this.options.length + options.length);
|
optionsLengthValidator.parse(this.options.length + options.length);
|
||||||
this.options.push(
|
this.options.push(
|
||||||
...options.map((option) =>
|
...options.map((option) =>
|
||||||
@@ -47,7 +49,8 @@ export class SelectMenuBuilder extends UnsafeSelectMenuBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override setOptions(options: (UnsafeSelectMenuOptionBuilder | APISelectMenuOption)[]) {
|
public override setOptions(...options: RestOrArray<UnsafeSelectMenuOptionBuilder | APISelectMenuOption>) {
|
||||||
|
options = normalizeArray(options);
|
||||||
optionsLengthValidator.parse(options.length);
|
optionsLengthValidator.parse(options.length);
|
||||||
this.options.splice(
|
this.options.splice(
|
||||||
0,
|
0,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { APISelectMenuOption, ComponentType, type APISelectMenuComponent } from 'discord-api-types/v10';
|
import { APISelectMenuOption, ComponentType, type APISelectMenuComponent } from 'discord-api-types/v10';
|
||||||
import { UnsafeSelectMenuOptionBuilder } from './UnsafeSelectMenuOption';
|
import { UnsafeSelectMenuOptionBuilder } from './UnsafeSelectMenuOption';
|
||||||
|
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
||||||
import { ComponentBuilder } from '../Component';
|
import { ComponentBuilder } from '../Component';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,9 +68,9 @@ export class UnsafeSelectMenuBuilder extends ComponentBuilder<APISelectMenuCompo
|
|||||||
* @param options The options to add to this select menu
|
* @param options The options to add to this select menu
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public addOptions(options: (UnsafeSelectMenuOptionBuilder | APISelectMenuOption)[]) {
|
public addOptions(...options: RestOrArray<UnsafeSelectMenuOptionBuilder | APISelectMenuOption>) {
|
||||||
this.options.push(
|
this.options.push(
|
||||||
...options.map((option) =>
|
...normalizeArray(options).map((option) =>
|
||||||
option instanceof UnsafeSelectMenuOptionBuilder ? option : new UnsafeSelectMenuOptionBuilder(option),
|
option instanceof UnsafeSelectMenuOptionBuilder ? option : new UnsafeSelectMenuOptionBuilder(option),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -80,11 +81,11 @@ export class UnsafeSelectMenuBuilder extends ComponentBuilder<APISelectMenuCompo
|
|||||||
* Sets the options on this select menu
|
* Sets the options on this select menu
|
||||||
* @param options The options to set on this select menu
|
* @param options The options to set on this select menu
|
||||||
*/
|
*/
|
||||||
public setOptions(options: (UnsafeSelectMenuOptionBuilder | APISelectMenuOption)[]) {
|
public setOptions(...options: RestOrArray<UnsafeSelectMenuOptionBuilder | APISelectMenuOption>) {
|
||||||
this.options.splice(
|
this.options.splice(
|
||||||
0,
|
0,
|
||||||
this.options.length,
|
this.options.length,
|
||||||
...options.map((option) =>
|
...normalizeArray(options).map((option) =>
|
||||||
option instanceof UnsafeSelectMenuOptionBuilder ? option : new UnsafeSelectMenuOptionBuilder(option),
|
option instanceof UnsafeSelectMenuOptionBuilder ? option : new UnsafeSelectMenuOptionBuilder(option),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -45,3 +45,4 @@ export * from './interactions/contextMenuCommands/ContextMenuCommandBuilder';
|
|||||||
export * from './util/jsonEncodable';
|
export * from './util/jsonEncodable';
|
||||||
export * from './util/equatable';
|
export * from './util/equatable';
|
||||||
export * from './util/componentUtil';
|
export * from './util/componentUtil';
|
||||||
|
export * from './util/normalizeArray';
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type {
|
|||||||
APIModalInteractionResponseCallbackData,
|
APIModalInteractionResponseCallbackData,
|
||||||
} from 'discord-api-types/v10';
|
} from 'discord-api-types/v10';
|
||||||
import { ActionRowBuilder, createComponentBuilder, JSONEncodable, ModalActionRowComponentBuilder } from '../../index';
|
import { ActionRowBuilder, createComponentBuilder, JSONEncodable, ModalActionRowComponentBuilder } from '../../index';
|
||||||
|
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
||||||
|
|
||||||
export class UnsafeModalBuilder implements JSONEncodable<APIModalInteractionResponseCallbackData> {
|
export class UnsafeModalBuilder implements JSONEncodable<APIModalInteractionResponseCallbackData> {
|
||||||
public readonly data: Partial<APIModalInteractionResponseCallbackData>;
|
public readonly data: Partial<APIModalInteractionResponseCallbackData>;
|
||||||
@@ -38,13 +39,12 @@ export class UnsafeModalBuilder implements JSONEncodable<APIModalInteractionResp
|
|||||||
* @param components The components to add to this modal
|
* @param components The components to add to this modal
|
||||||
*/
|
*/
|
||||||
public addComponents(
|
public addComponents(
|
||||||
components: (
|
...components: RestOrArray<
|
||||||
| ActionRowBuilder<ModalActionRowComponentBuilder>
|
ActionRowBuilder<ModalActionRowComponentBuilder> | APIActionRowComponent<APIModalActionRowComponent>
|
||||||
| APIActionRowComponent<APIModalActionRowComponent>
|
>
|
||||||
)[],
|
|
||||||
) {
|
) {
|
||||||
this.components.push(
|
this.components.push(
|
||||||
...components.map((component) =>
|
...normalizeArray(components).map((component) =>
|
||||||
component instanceof ActionRowBuilder
|
component instanceof ActionRowBuilder
|
||||||
? component
|
? component
|
||||||
: new ActionRowBuilder<ModalActionRowComponentBuilder>(component),
|
: new ActionRowBuilder<ModalActionRowComponentBuilder>(component),
|
||||||
@@ -57,8 +57,8 @@ export class UnsafeModalBuilder implements JSONEncodable<APIModalInteractionResp
|
|||||||
* Sets the components in this modal
|
* Sets the components in this modal
|
||||||
* @param components The components to set this modal to
|
* @param components The components to set this modal to
|
||||||
*/
|
*/
|
||||||
public setComponents(components: ActionRowBuilder<ModalActionRowComponentBuilder>[]) {
|
public setComponents(...components: RestOrArray<ActionRowBuilder<ModalActionRowComponentBuilder>>) {
|
||||||
this.components.splice(0, this.components.length, ...components);
|
this.components.splice(0, this.components.length, ...normalizeArray(components));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,17 +12,19 @@ import {
|
|||||||
validateFieldLength,
|
validateFieldLength,
|
||||||
} from './Assertions';
|
} from './Assertions';
|
||||||
import { EmbedAuthorOptions, EmbedFooterOptions, RGBTuple, UnsafeEmbedBuilder } from './UnsafeEmbed';
|
import { EmbedAuthorOptions, EmbedFooterOptions, RGBTuple, UnsafeEmbedBuilder } from './UnsafeEmbed';
|
||||||
|
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a validated embed in a message (image/video preview, rich embed, etc.)
|
* Represents a validated embed in a message (image/video preview, rich embed, etc.)
|
||||||
*/
|
*/
|
||||||
export class EmbedBuilder extends UnsafeEmbedBuilder {
|
export class EmbedBuilder extends UnsafeEmbedBuilder {
|
||||||
public override addFields(fields: APIEmbedField[]): this {
|
public override addFields(...fields: RestOrArray<APIEmbedField>): this {
|
||||||
|
fields = normalizeArray(fields);
|
||||||
// Ensure adding these fields won't exceed the 25 field limit
|
// Ensure adding these fields won't exceed the 25 field limit
|
||||||
validateFieldLength(fields.length, this.data.fields);
|
validateFieldLength(fields.length, this.data.fields);
|
||||||
|
|
||||||
// Data assertions
|
// Data assertions
|
||||||
return super.addFields(embedFieldsArrayPredicate.parse(fields));
|
return super.addFields(...embedFieldsArrayPredicate.parse(fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override spliceFields(index: number, deleteCount: number, ...fields: APIEmbedField[]): this {
|
public override spliceFields(index: number, deleteCount: number, ...fields: APIEmbedField[]): this {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { APIEmbed, APIEmbedAuthor, APIEmbedField, APIEmbedFooter, APIEmbedImage } from 'discord-api-types/v10';
|
import type { APIEmbed, APIEmbedAuthor, APIEmbedField, APIEmbedFooter, APIEmbedImage } from 'discord-api-types/v10';
|
||||||
|
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray';
|
||||||
|
|
||||||
export type RGBTuple = [red: number, green: number, blue: number];
|
export type RGBTuple = [red: number, green: number, blue: number];
|
||||||
|
|
||||||
@@ -44,7 +45,8 @@ export class UnsafeEmbedBuilder {
|
|||||||
*
|
*
|
||||||
* @param fields The fields to add
|
* @param fields The fields to add
|
||||||
*/
|
*/
|
||||||
public addFields(fields: APIEmbedField[]): this {
|
public addFields(...fields: RestOrArray<APIEmbedField>): this {
|
||||||
|
fields = normalizeArray(fields);
|
||||||
if (this.data.fields) this.data.fields.push(...fields);
|
if (this.data.fields) this.data.fields.push(...fields);
|
||||||
else this.data.fields = fields;
|
else this.data.fields = fields;
|
||||||
return this;
|
return this;
|
||||||
@@ -67,8 +69,8 @@ export class UnsafeEmbedBuilder {
|
|||||||
* Sets the embed's fields (max 25).
|
* Sets the embed's fields (max 25).
|
||||||
* @param fields The fields to set
|
* @param fields The fields to set
|
||||||
*/
|
*/
|
||||||
public setFields(fields: APIEmbedField[]) {
|
public setFields(...fields: RestOrArray<APIEmbedField>) {
|
||||||
this.spliceFields(0, this.data.fields?.length ?? 0, ...fields);
|
this.spliceFields(0, this.data.fields?.length ?? 0, ...normalizeArray(fields));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
packages/builders/src/util/normalizeArray.ts
Normal file
6
packages/builders/src/util/normalizeArray.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export function normalizeArray<T>(arr: RestOrArray<T>): T[] {
|
||||||
|
if (Array.isArray(arr[0])) return arr[0];
|
||||||
|
return arr as T[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RestOrArray<T> = T[] | [T[]];
|
||||||
5
packages/discord.js/typings/index.d.ts
vendored
5
packages/discord.js/typings/index.d.ts
vendored
@@ -29,6 +29,7 @@ import {
|
|||||||
ModalBuilder as BuildersModal,
|
ModalBuilder as BuildersModal,
|
||||||
AnyComponentBuilder,
|
AnyComponentBuilder,
|
||||||
ComponentBuilder,
|
ComponentBuilder,
|
||||||
|
type RestOrArray,
|
||||||
} from '@discordjs/builders';
|
} from '@discordjs/builders';
|
||||||
import { Collection } from '@discordjs/collection';
|
import { Collection } from '@discordjs/collection';
|
||||||
import { BaseImageURLOptions, ImageURLOptions, RawFile, REST, RESTOptions } from '@discordjs/rest';
|
import { BaseImageURLOptions, ImageURLOptions, RawFile, REST, RESTOptions } from '@discordjs/rest';
|
||||||
@@ -608,10 +609,10 @@ export class ButtonBuilder extends BuilderButtonComponent {
|
|||||||
export class SelectMenuBuilder extends BuilderSelectMenuComponent {
|
export class SelectMenuBuilder extends BuilderSelectMenuComponent {
|
||||||
public constructor(data?: Partial<SelectMenuComponentData | APISelectMenuComponent>);
|
public constructor(data?: Partial<SelectMenuComponentData | APISelectMenuComponent>);
|
||||||
public override addOptions(
|
public override addOptions(
|
||||||
options: (BuildersSelectMenuOption | SelectMenuComponentOptionData | APISelectMenuOption)[],
|
...options: RestOrArray<BuildersSelectMenuOption | SelectMenuComponentOptionData | APISelectMenuOption>
|
||||||
): this;
|
): this;
|
||||||
public override setOptions(
|
public override setOptions(
|
||||||
options: (BuildersSelectMenuOption | SelectMenuComponentOptionData | APISelectMenuOption)[],
|
...options: RestOrArray<BuildersSelectMenuOption | SelectMenuComponentOptionData | APISelectMenuOption>
|
||||||
): this;
|
): this;
|
||||||
public static from(other: JSONEncodable<APISelectMenuComponent> | APISelectMenuComponent): SelectMenuBuilder;
|
public static from(other: JSONEncodable<APISelectMenuComponent> | APISelectMenuComponent): SelectMenuBuilder;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user