feat!: More label components and text display in modal (#11078)

BREAKING CHANGE: Modals only have adding (no setting) and splicing has been replaced with a generalised splice method to support all components.
This commit is contained in:
Jiralite
2025-09-14 09:09:56 +01:00
committed by GitHub
parent 126529f460
commit b66f52f9aa
9 changed files with 157 additions and 64 deletions

View File

@@ -89,6 +89,11 @@ export type ModalActionRowComponentBuilder = TextInputBuilder;
*/
export type AnyActionRowComponentBuilder = MessageActionRowComponentBuilder | ModalActionRowComponentBuilder;
/**
* Any modal component builder.
*/
export type AnyModalComponentBuilder = LabelBuilder | TextDisplayBuilder;
/**
* Components here are mapped to their respective builder.
*/

View File

@@ -1,11 +1,24 @@
import { ComponentType } from 'discord-api-types/v10';
import { z } from 'zod';
import { selectMenuStringPredicate } from '../Assertions';
import {
selectMenuChannelPredicate,
selectMenuMentionablePredicate,
selectMenuRolePredicate,
selectMenuStringPredicate,
selectMenuUserPredicate,
} from '../Assertions';
import { textInputPredicate } from '../textInput/Assertions';
export const labelPredicate = z.object({
type: z.literal(ComponentType.Label),
label: z.string().min(1).max(45),
description: z.string().min(1).max(100).optional(),
component: z.union([selectMenuStringPredicate, textInputPredicate]),
component: z.union([
selectMenuStringPredicate,
textInputPredicate,
selectMenuUserPredicate,
selectMenuRolePredicate,
selectMenuMentionablePredicate,
selectMenuChannelPredicate,
]),
});

View File

@@ -1,15 +1,33 @@
import type { APILabelComponent, APIStringSelectComponent, APITextInputComponent } from 'discord-api-types/v10';
import type {
APIChannelSelectComponent,
APILabelComponent,
APIMentionableSelectComponent,
APIRoleSelectComponent,
APIStringSelectComponent,
APITextInputComponent,
APIUserSelectComponent,
} from 'discord-api-types/v10';
import { ComponentType } from 'discord-api-types/v10';
import { resolveBuilder } from '../../util/resolveBuilder.js';
import { validate } from '../../util/validation.js';
import { ComponentBuilder } from '../Component.js';
import { createComponentBuilder } from '../Components.js';
import { ChannelSelectMenuBuilder } from '../selectMenu/ChannelSelectMenu.js';
import { MentionableSelectMenuBuilder } from '../selectMenu/MentionableSelectMenu.js';
import { RoleSelectMenuBuilder } from '../selectMenu/RoleSelectMenu.js';
import { StringSelectMenuBuilder } from '../selectMenu/StringSelectMenu.js';
import { UserSelectMenuBuilder } from '../selectMenu/UserSelectMenu.js';
import { TextInputBuilder } from '../textInput/TextInput.js';
import { labelPredicate } from './Assertions.js';
export interface LabelBuilderData extends Partial<Omit<APILabelComponent, 'component'>> {
component?: StringSelectMenuBuilder | TextInputBuilder;
component?:
| ChannelSelectMenuBuilder
| MentionableSelectMenuBuilder
| RoleSelectMenuBuilder
| StringSelectMenuBuilder
| TextInputBuilder
| UserSelectMenuBuilder;
}
/**
@@ -49,7 +67,6 @@ export class LabelBuilder extends ComponentBuilder<APILabelComponent> {
this.data = {
...structuredClone(rest),
// @ts-expect-error https://github.com/discordjs/discord.js/pull/11078
component: component ? createComponentBuilder(component) : undefined,
type: ComponentType.Label,
};
@@ -98,6 +115,60 @@ export class LabelBuilder extends ComponentBuilder<APILabelComponent> {
return this;
}
/**
* Sets a user select menu component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setUserSelectMenuComponent(
input: APIUserSelectComponent | UserSelectMenuBuilder | ((builder: UserSelectMenuBuilder) => UserSelectMenuBuilder),
): this {
this.data.component = resolveBuilder(input, UserSelectMenuBuilder);
return this;
}
/**
* Sets a role select menu component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setRoleSelectMenuComponent(
input: APIRoleSelectComponent | RoleSelectMenuBuilder | ((builder: RoleSelectMenuBuilder) => RoleSelectMenuBuilder),
): this {
this.data.component = resolveBuilder(input, RoleSelectMenuBuilder);
return this;
}
/**
* Sets a mentionable select menu component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setMentionableSelectMenuComponent(
input:
| APIMentionableSelectComponent
| MentionableSelectMenuBuilder
| ((builder: MentionableSelectMenuBuilder) => MentionableSelectMenuBuilder),
): this {
this.data.component = resolveBuilder(input, MentionableSelectMenuBuilder);
return this;
}
/**
* Sets a channel select menu component to this label.
*
* @param input - A function that returns a component builder or an already built builder
*/
public setChannelSelectMenuComponent(
input:
| APIChannelSelectComponent
| ChannelSelectMenuBuilder
| ((builder: ChannelSelectMenuBuilder) => ChannelSelectMenuBuilder),
): this {
this.data.component = resolveBuilder(input, ChannelSelectMenuBuilder);
return this;
}
/**
* Sets a text input component to this label.
*
@@ -118,6 +189,7 @@ export class LabelBuilder extends ComponentBuilder<APILabelComponent> {
const data = {
...structuredClone(rest),
// The label predicate validates the component.
component: component?.toJSON(false),
};

View File

@@ -15,7 +15,7 @@ export abstract class BaseSelectMenuBuilder<Data extends APISelectMenuComponent>
* @internal
*/
protected abstract override readonly data: Partial<
Pick<Data, 'custom_id' | 'disabled' | 'id' | 'max_values' | 'min_values' | 'placeholder'>
Pick<Data, 'custom_id' | 'disabled' | 'id' | 'max_values' | 'min_values' | 'placeholder' | 'required'>
>;
/**
@@ -75,4 +75,15 @@ export abstract class BaseSelectMenuBuilder<Data extends APISelectMenuComponent>
this.data.disabled = disabled;
return this;
}
/**
* Sets whether this select menu is required.
*
* @remarks Only for use in modals.
* @param required - Whether this string select menu is required
*/
public setRequired(required = true) {
this.data.required = required;
return this;
}
}

View File

@@ -147,17 +147,6 @@ export class StringSelectMenuBuilder extends BaseSelectMenuBuilder<APIStringSele
return this;
}
/**
* Sets whether this string select menu is required.
*
* @remarks Only for use in modals.
* @param required - Whether this string select menu is required
*/
public setRequired(required = true) {
this.data.required = required;
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/