Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/builders/src/interactions/modals/Assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { s } from '@sapphire/shapeshift';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
import { customIdValidator } from '../../components/Assertions.js';
import { LabelBuilder } from '../../components/label/Label.js';
import { TextDisplayBuilder } from '../../components/v2/TextDisplay.js';
import { isValidationEnabled } from '../../util/validation.js';

export const titleValidator = s
Expand All @@ -10,15 +11,15 @@ export const titleValidator = s
.lengthLessThanOrEqual(45)
.setValidationEnabled(isValidationEnabled);
export const componentsValidator = s
.union([s.instance(ActionRowBuilder), s.instance(LabelBuilder)])
.union([s.instance(ActionRowBuilder), s.instance(LabelBuilder), s.instance(TextDisplayBuilder)])
.array()
.lengthGreaterThanOrEqual(1)
.setValidationEnabled(isValidationEnabled);

export function validateRequiredParameters(
customId?: string,
title?: string,
components?: (ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder)[],
components?: (ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder | TextDisplayBuilder)[],
) {
customIdValidator.parse(customId);
titleValidator.parse(title);
Expand Down
43 changes: 39 additions & 4 deletions packages/builders/src/interactions/modals/Modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import type {
APIComponentInModalActionRow,
APILabelComponent,
APIModalInteractionResponseCallbackData,
APITextDisplayComponent,
} from 'discord-api-types/v10';
import { ComponentType } from 'discord-api-types/v10';
import { ActionRowBuilder, type ModalActionRowComponentBuilder } from '../../components/ActionRow.js';
import { customIdValidator } from '../../components/Assertions.js';
import { createComponentBuilder, resolveBuilder } from '../../components/Components.js';
import { LabelBuilder } from '../../components/label/Label.js';
import { TextInputBuilder } from '../../components/textInput/TextInput.js';
import { TextDisplayBuilder } from '../../components/v2/TextDisplay.js';
import { normalizeArray, type RestOrArray } from '../../util/normalizeArray.js';
import { titleValidator, validateRequiredParameters } from './Assertions.js';

Expand All @@ -29,7 +31,8 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
/**
* The components within this modal.
*/
public readonly components: (ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder)[] = [];
public readonly components: (ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder | TextDisplayBuilder)[] =
[];

/**
* Creates a new modal from API data.
Expand Down Expand Up @@ -68,24 +71,31 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
* Adds components to this modal.
*
* @param components - The components to add
* @deprecated Use {@link ModalBuilder.addLabelComponents} or {@link ModalBuilder.addActionRowComponents} instead
* @deprecated Use {@link ModalBuilder.addLabelComponents} or {@link ModalBuilder.addTextDisplayComponents} instead
*/
public addComponents(
...components: RestOrArray<
| ActionRowBuilder<ModalActionRowComponentBuilder>
| APIActionRowComponent<APIComponentInModalActionRow>
| APILabelComponent
| APITextDisplayComponent
| APITextInputComponent
| LabelBuilder
| TextDisplayBuilder
| TextInputBuilder
>
) {
this.components.push(
...normalizeArray(components).map((component, idx) => {
if (component instanceof ActionRowBuilder || component instanceof LabelBuilder) {
if (
component instanceof ActionRowBuilder ||
component instanceof LabelBuilder ||
component instanceof TextDisplayBuilder
) {
return component;
}

// Deprecated support
if (component instanceof TextInputBuilder) {
return new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(component);
}
Expand All @@ -99,6 +109,11 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
return new LabelBuilder(component);
}

if (component.type === ComponentType.TextDisplay) {
return new TextDisplayBuilder(component);
}

// Deprecated, should go in a label component
if (component.type === ComponentType.TextInput) {
return new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
new TextInputBuilder(component),
Expand Down Expand Up @@ -128,6 +143,24 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
return this;
}

/**
* Adds text display components to this modal.
*
* @param components - The components to add
*/
public addTextDisplayComponents(
...components: RestOrArray<
APITextDisplayComponent | TextDisplayBuilder | ((builder: TextDisplayBuilder) => TextDisplayBuilder)
>
) {
const normalized = normalizeArray(components);
const resolved = normalized.map((row) => resolveBuilder(row, TextDisplayBuilder));

this.components.push(...resolved);

return this;
}

/**
* Adds action rows to this modal.
*
Expand Down Expand Up @@ -211,7 +244,9 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
* @param components - The components to set
* @deprecated Use {@link ModalBuilder.setLabelComponents} instead
*/
public setComponents(...components: RestOrArray<ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder>) {
public setComponents(
...components: RestOrArray<ActionRowBuilder<ModalActionRowComponentBuilder> | LabelBuilder | TextDisplayBuilder>
) {
this.components.splice(0, this.components.length, ...normalizeArray(components));
return this;
}
Expand Down