From 589eb1fef281258c033a0bab8938556dccf67395 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Wed, 6 Aug 2025 17:54:04 -0400 Subject: [PATCH 01/19] Update Usage for SelectMenu and StringSelect --- src/Discord/Builders/Components/SelectMenu.php | 2 +- src/Discord/Builders/Components/StringSelect.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Discord/Builders/Components/SelectMenu.php b/src/Discord/Builders/Components/SelectMenu.php index 667a234d6..ff17e9c5c 100644 --- a/src/Discord/Builders/Components/SelectMenu.php +++ b/src/Discord/Builders/Components/SelectMenu.php @@ -35,7 +35,7 @@ */ abstract class SelectMenu extends Interactive { - public const USAGE = ['Message']; + public const USAGE = ['Message', 'Modal']; /** * Component type. diff --git a/src/Discord/Builders/Components/StringSelect.php b/src/Discord/Builders/Components/StringSelect.php index 33595f21d..712caa2a2 100644 --- a/src/Discord/Builders/Components/StringSelect.php +++ b/src/Discord/Builders/Components/StringSelect.php @@ -23,7 +23,7 @@ */ class StringSelect extends SelectMenu { - public const USAGE = ['Message']; + public const USAGE = ['Message', 'Modal']; /** * Component type. From 28264ef3dab397331b578a1273dc66afbf699f21 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Wed, 6 Aug 2025 17:57:15 -0400 Subject: [PATCH 02/19] Component::TYPE_LABEL = 18 --- src/Discord/Builders/Components/Component.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord/Builders/Components/Component.php b/src/Discord/Builders/Components/Component.php index 28718319b..1cd4a2bf0 100644 --- a/src/Discord/Builders/Components/Component.php +++ b/src/Discord/Builders/Components/Component.php @@ -43,6 +43,7 @@ abstract class Component implements JsonSerializable public const TYPE_SEPARATOR = 14; public const TYPE_CONTENT_INVENTORY_ENTRY = 16; // Not documented public const TYPE_CONTAINER = 17; + public const TYPE_LABEL = 18; /** @deprecated 7.4.0 Use `Component::TYPE_STRING_SELECT` */ public const TYPE_SELECT_MENU = 3; From 5c9ab8f4c3c5434433045dbdf0af1891025afb98 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Wed, 6 Aug 2025 18:12:35 -0400 Subject: [PATCH 03/19] SelectMenu::required (Modal only) --- .../Builders/Components/SelectMenu.php | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Discord/Builders/Components/SelectMenu.php b/src/Discord/Builders/Components/SelectMenu.php index ff17e9c5c..f38ec43b7 100644 --- a/src/Discord/Builders/Components/SelectMenu.php +++ b/src/Discord/Builders/Components/SelectMenu.php @@ -103,6 +103,13 @@ abstract class SelectMenu extends Interactive */ protected $disabled; + /** + * Whether the select menu is required. (Modal only) + * + * @var bool|null + */ + protected $required; + /** * Callback used to listen for `INTERACTION_CREATE` events. * @@ -309,6 +316,20 @@ public function setDisabled(bool $disabled = true): self return $this; } + /** + * Sets whether the select menu is required. (Modal only) + * + * @param bool|null $required + * + * @return $this + */ + public function setRequired(?bool $required = false): self + { + $this->required = $required; + + return $this; + } + /** * Sets the callable listener for the select menu. The `$callback` function * will be called when the selection of the menu is changed. @@ -538,7 +559,7 @@ public function jsonSerialize(): array $content['min_values'] = $this->min_values; } - if ($this->max_values) { + if (isset($this->max_values) && $this->max_values) { if (isset($this->options) && $this->max_values > count($this->options)) { throw new \OutOfBoundsException('There are less options than the maximum number of options to be selected.'); } @@ -546,10 +567,14 @@ public function jsonSerialize(): array $content['max_values'] = $this->max_values; } - if ($this->disabled) { + if (isset($this->disabled) && $this->disabled) { $content['disabled'] = true; } + if (isset($this->required)) { + $content['required'] = true; + } + return $content; } } From d6196d681f7622b972cd3c5b513aa349eb593274 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Wed, 6 Aug 2025 18:18:33 -0400 Subject: [PATCH 04/19] Add Label to Component::TYPES --- src/Discord/Parts/Channel/Message/Component.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord/Parts/Channel/Message/Component.php b/src/Discord/Parts/Channel/Message/Component.php index e55dc89a7..6a1011653 100644 --- a/src/Discord/Parts/Channel/Message/Component.php +++ b/src/Discord/Parts/Channel/Message/Component.php @@ -56,6 +56,7 @@ class Component extends Part ComponentBuilder::TYPE_FILE => File::class, ComponentBuilder::TYPE_SEPARATOR => Separator::class, ComponentBuilder::TYPE_CONTAINER => Container::class, + ComponentBuilder::TYPE_LABEL => Label::class, ]; /** From 675908d8cf0cc8fb306294f5895273bacca24e8e Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Wed, 6 Aug 2025 18:25:56 -0400 Subject: [PATCH 05/19] Create Label Part --- src/Discord/Parts/Channel/Message/Label.php | 52 +++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/Discord/Parts/Channel/Message/Label.php diff --git a/src/Discord/Parts/Channel/Message/Label.php b/src/Discord/Parts/Channel/Message/Label.php new file mode 100644 index 000000000..a5739682d --- /dev/null +++ b/src/Discord/Parts/Channel/Message/Label.php @@ -0,0 +1,52 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\Parts\Channel\Message; + +/** + * A Label is a top-level component. + * + * @link https://discord.com/developers/docs/components/reference#label + * + * @todo Update to match Discord's documentation upon public release. + * @todo Update Label class to extend the relevant base class. + * + * @since 10.19.0 + * + * @property int $type 18 for label component. + * @property string $label The text for the label. + * @property string|null $description Optional description for the label. + * @property StringSelect|TextInput $component The component associated with the label. + */ +class Label extends Component +{ + /** + * {@inheritDoc} + */ + protected $fillable = [ + 'id', + 'type', + 'label', + 'description', + 'component', + ]; + + public function getComponentAttribute(): StringSelect|TextInput|null + { + if (!isset($this->attributes['component'])) { + return null; + } + + return $this->createOf(Component::TYPES[$this->attributes['component']->type ?? 0], $this->attributes['component']); + } +} From f7b2d4f0825ed04076a31d8b9841fa3ba55f7dea Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Wed, 6 Aug 2025 18:38:24 -0400 Subject: [PATCH 06/19] Create Label Builder --- src/Discord/Builders/Components/Label.php | 144 ++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 src/Discord/Builders/Components/Label.php diff --git a/src/Discord/Builders/Components/Label.php b/src/Discord/Builders/Components/Label.php new file mode 100644 index 000000000..f7640dd4c --- /dev/null +++ b/src/Discord/Builders/Components/Label.php @@ -0,0 +1,144 @@ + + * + * This file is subject to the MIT license that is bundled + * with this source code in the LICENSE.md file. + */ + +namespace Discord\Builders\Components; + +use Discord\Builders\Components\StringSelect; + +/** + * A Label is a top-level component. + * + * @link https://discord.com/developers/docs/components/reference#label + * + * @todo Update to match Discord's documentation upon public release. + * @todo Update Label class to extend the relevant base class. + * @todo Confirm if Label will be usable in Message components. + * + * @since 10.19.0 + * + * @property int $type 18 for label component. + * @property string $label The text for the label. + * @property string|null $description Optional description for the label. + * @property StringSelect|TextInput $component The component associated with the label. + */ +class Label extends ComponentObject +{ + public const USAGE = ['Modal']; + + /** + * Component type. + * + * @var int + */ + protected $type = Component::TYPE_LABEL; + + /** + * The text for the label. + * + * @var string + */ + protected $label; + + /** + * Optional description for the label. + * + * @var string|null + */ + protected $description; + + /** + * The component associated with the label. + * + * @var StringSelect|TextInput + */ + protected $component; + + /** + * Creates a new label component. + * + * @param string $label The text for the label. + * @param StringSelect|TextInput $component The component associated with the label. + * @param string|null $description Optional description for the label. + * + * @return self + */ + public static function new(string $label, StringSelect|TextInput $component, ?string $description = null): self + { + $label_component = new self(); + + $label_component->setLabel($label); + $label_component->setComponent($component); + $label_component->setDescription($description); + + return $label_component; + } + + /** + * Sets the label text. + * + * @param string $label The text for the label. + * + * @return self + */ + public function setLabel(string $label): self + { + $this->label = $label; + + return $this; + } + + /** + * Sets the description text. + * + * @param string|null $description The description for the label. + * + * @return self + */ + public function setDescription(string|null $description): self + { + $this->description = $description; + + return $this; + } + + /** Sets the component associated with the label. + * + * @param StringSelect|TextInput $component The component associated with the label. + * + * @return self + */ + public function setComponent(StringSelect|TextInput $component): self + { + $this->component = $component; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function jsonSerialize(): array + { + $data = [ + 'type' => $this->type, + 'label' => $this->label, + 'component' => $this->component + ]; + + if (isset($this->description)) { + $data['description'] = $this->description; + } + + return $data; + } +} From de736aa5023c4b7eed5bdf6a7806f879ccc458c7 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Wed, 6 Aug 2025 18:38:46 -0400 Subject: [PATCH 07/19] Deprecate TextInput::label Use top-level label component instead --- src/Discord/Builders/Components/TextInput.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Discord/Builders/Components/TextInput.php b/src/Discord/Builders/Components/TextInput.php index f47c747f0..c7f5384cd 100644 --- a/src/Discord/Builders/Components/TextInput.php +++ b/src/Discord/Builders/Components/TextInput.php @@ -46,7 +46,9 @@ class TextInput extends Interactive /** * Label for the text input. * - * @var string + * @var string|null + * + * @deprecated Use top-level Component::Label instead. */ private $label; @@ -92,7 +94,7 @@ class TextInput extends Interactive * @param int $style The style of the text input. * @param string|null $custom_id The custom ID of the text input. If not given, a UUID will be used */ - public function __construct(string $label, int $style, ?string $custom_id = null) + public function __construct(?string $label = null, int $style, ?string $custom_id = null) { $this->setLabel($label); $this->setStyle($style); @@ -108,7 +110,7 @@ public function __construct(string $label, int $style, ?string $custom_id = null * * @return self */ - public static function new(string $label, int $style, ?string $custom_id = null): self + public static function new(?string $label = null, int $style, ?string $custom_id = null): self { return new self($label, $style, $custom_id); } @@ -156,15 +158,15 @@ public function setStyle(int $style): self /** * Sets the label of the text input. * - * @param string $label Label of the text input. Maximum 45 characters. + * @param string|null $label Label of the text input. Maximum 45 characters. * * @throws \LengthException * * @return $this */ - public function setLabel(string $label): self + public function setLabel(?string $label = null): self { - if (poly_strlen($label) > 45) { + if (isset($label) && poly_strlen($label) > 45) { throw new \LengthException('Label must be maximum 45 characters.'); } @@ -326,9 +328,12 @@ public function jsonSerialize(): array 'type' => $this->type, 'custom_id' => $this->custom_id, 'style' => $this->style, - 'label' => $this->label, ]; + if (isset($this->label)) { + $content['label'] = $this->label; + } + if (isset($this->min_length)) { $content['min_length'] = $this->min_length; } From ec2fdd4e2ffc79e8a9b902687bc8a6aa071af27e Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Wed, 6 Aug 2025 18:46:38 -0400 Subject: [PATCH 08/19] Ran CS Fixer --- src/Discord/Builders/Components/Label.php | 12 +++++------- src/Discord/Builders/Components/SelectMenu.php | 8 ++++---- src/Discord/Builders/Components/TextInput.php | 2 +- src/Discord/Parts/Channel/Message/Component.php | 2 +- src/Discord/Parts/Channel/Message/Label.php | 4 ++-- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/Discord/Builders/Components/Label.php b/src/Discord/Builders/Components/Label.php index f7640dd4c..24d920031 100644 --- a/src/Discord/Builders/Components/Label.php +++ b/src/Discord/Builders/Components/Label.php @@ -13,8 +13,6 @@ namespace Discord\Builders\Components; -use Discord\Builders\Components\StringSelect; - /** * A Label is a top-level component. * @@ -63,11 +61,11 @@ class Label extends ComponentObject */ protected $component; - /** + /** * Creates a new label component. * - * @param string $label The text for the label. - * @param StringSelect|TextInput $component The component associated with the label. + * @param string $label The text for the label. + * @param StringSelect|TextInput $component The component associated with the label. * @param string|null $description Optional description for the label. * * @return self @@ -125,14 +123,14 @@ public function setComponent(StringSelect|TextInput $component): self } /** - * {@inheritDoc} + * @inheritDoc */ public function jsonSerialize(): array { $data = [ 'type' => $this->type, 'label' => $this->label, - 'component' => $this->component + 'component' => $this->component, ]; if (isset($this->description)) { diff --git a/src/Discord/Builders/Components/SelectMenu.php b/src/Discord/Builders/Components/SelectMenu.php index f38ec43b7..5606b61d2 100644 --- a/src/Discord/Builders/Components/SelectMenu.php +++ b/src/Discord/Builders/Components/SelectMenu.php @@ -104,7 +104,7 @@ abstract class SelectMenu extends Interactive protected $disabled; /** - * Whether the select menu is required. (Modal only) + * Whether the select menu is required. (Modal only). * * @var bool|null */ @@ -316,8 +316,8 @@ public function setDisabled(bool $disabled = true): self return $this; } - /** - * Sets whether the select menu is required. (Modal only) + /** + * Sets whether the select menu is required. (Modal only). * * @param bool|null $required * @@ -526,7 +526,7 @@ public function isDisabled(): ?bool } /** - * {@inheritDoc} + * @inheritDoc */ public function jsonSerialize(): array { diff --git a/src/Discord/Builders/Components/TextInput.php b/src/Discord/Builders/Components/TextInput.php index c7f5384cd..80e70a07e 100644 --- a/src/Discord/Builders/Components/TextInput.php +++ b/src/Discord/Builders/Components/TextInput.php @@ -320,7 +320,7 @@ public function isRequired(): ?bool } /** - * {@inheritDoc} + * @inheritDoc */ public function jsonSerialize(): array { diff --git a/src/Discord/Parts/Channel/Message/Component.php b/src/Discord/Parts/Channel/Message/Component.php index 6a1011653..aa5df24eb 100644 --- a/src/Discord/Parts/Channel/Message/Component.php +++ b/src/Discord/Parts/Channel/Message/Component.php @@ -60,7 +60,7 @@ class Component extends Part ]; /** - * {@inheritDoc} + * @inheritDoc */ protected $fillable = [ 'type', diff --git a/src/Discord/Parts/Channel/Message/Label.php b/src/Discord/Parts/Channel/Message/Label.php index a5739682d..44cf1e18e 100644 --- a/src/Discord/Parts/Channel/Message/Label.php +++ b/src/Discord/Parts/Channel/Message/Label.php @@ -31,7 +31,7 @@ class Label extends Component { /** - * {@inheritDoc} + * @inheritDoc */ protected $fillable = [ 'id', @@ -43,7 +43,7 @@ class Label extends Component public function getComponentAttribute(): StringSelect|TextInput|null { - if (!isset($this->attributes['component'])) { + if (! isset($this->attributes['component'])) { return null; } From bdbc223a523113f23437e0cdc159bc84aaea3a93 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 7 Aug 2025 07:32:07 -0400 Subject: [PATCH 09/19] Update Label.php --- src/Discord/Builders/Components/Label.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Discord/Builders/Components/Label.php b/src/Discord/Builders/Components/Label.php index 24d920031..a338ff158 100644 --- a/src/Discord/Builders/Components/Label.php +++ b/src/Discord/Builders/Components/Label.php @@ -70,7 +70,7 @@ class Label extends ComponentObject * * @return self */ - public static function new(string $label, StringSelect|TextInput $component, ?string $description = null): self + public static function new(string $label, $component, ?string $description = null): self { $label_component = new self(); @@ -102,7 +102,7 @@ public function setLabel(string $label): self * * @return self */ - public function setDescription(string|null $description): self + public function setDescription(?string $description = null): self { $this->description = $description; @@ -115,8 +115,12 @@ public function setDescription(string|null $description): self * * @return self */ - public function setComponent(StringSelect|TextInput $component): self + public function setComponent($component): self { + if (!in_array($component->type, [Component::TYPE_STRING_SELECT, Component::TYPE_TEXT_INPUT], true)) { + throw new \InvalidArgumentException('Component must be a StringSelect or TextInput.'); + } + $this->component = $component; return $this; From 318608aa3e1271967a10175d646f14f29c19b3f7 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 7 Aug 2025 07:32:57 -0400 Subject: [PATCH 10/19] Update Label.php --- src/Discord/Parts/Channel/Message/Label.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Discord/Parts/Channel/Message/Label.php b/src/Discord/Parts/Channel/Message/Label.php index 44cf1e18e..afc39eb1e 100644 --- a/src/Discord/Parts/Channel/Message/Label.php +++ b/src/Discord/Parts/Channel/Message/Label.php @@ -41,12 +41,8 @@ class Label extends Component 'component', ]; - public function getComponentAttribute(): StringSelect|TextInput|null + public function getComponentAttribute(): StringSelect|TextInput { - if (! isset($this->attributes['component'])) { - return null; - } - return $this->createOf(Component::TYPES[$this->attributes['component']->type ?? 0], $this->attributes['component']); } } From 61babf145e32d1379bc23c46737ddd97de6c48a8 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 7 Aug 2025 07:36:27 -0400 Subject: [PATCH 11/19] Update TextInput.php --- src/Discord/Builders/Components/TextInput.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Discord/Builders/Components/TextInput.php b/src/Discord/Builders/Components/TextInput.php index 80e70a07e..de8c3373b 100644 --- a/src/Discord/Builders/Components/TextInput.php +++ b/src/Discord/Builders/Components/TextInput.php @@ -46,9 +46,10 @@ class TextInput extends Interactive /** * Label for the text input. * + * Deprecated for use with modals. Use a top-level Component::Label. + * * @var string|null * - * @deprecated Use top-level Component::Label instead. */ private $label; From 5e487c90c04b618c3283e9b3146be069dad3309b Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 7 Aug 2025 07:48:01 -0400 Subject: [PATCH 12/19] Update ActionRow.php --- src/Discord/Parts/Channel/Message/ActionRow.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Discord/Parts/Channel/Message/ActionRow.php b/src/Discord/Parts/Channel/Message/ActionRow.php index 7fe3b8228..116671ead 100644 --- a/src/Discord/Parts/Channel/Message/ActionRow.php +++ b/src/Discord/Parts/Channel/Message/ActionRow.php @@ -14,10 +14,12 @@ namespace Discord\Parts\Channel\Message; /** - * An Action Row is a top-level layout component used in messages and modals. + * An Action Row is a top-level layout component used in messages. + * + * Using ActionRows in modals is now deprecated - use Component::Label as the top level component! * * Action Rows can contain: - + * * Up to 5 contextually grouped buttons * A single text input * A single select component (string select, user select, role select, mentionable select, or channel select) From fd4696ac3b6953acae2b0da59b9542d1d3ae9890 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 7 Aug 2025 08:49:02 -0400 Subject: [PATCH 13/19] Remove component type check It's valid to pass a Part instead of a Builder --- src/Discord/Builders/Components/Label.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Discord/Builders/Components/Label.php b/src/Discord/Builders/Components/Label.php index a338ff158..5b07d2f4c 100644 --- a/src/Discord/Builders/Components/Label.php +++ b/src/Discord/Builders/Components/Label.php @@ -117,10 +117,6 @@ public function setDescription(?string $description = null): self */ public function setComponent($component): self { - if (!in_array($component->type, [Component::TYPE_STRING_SELECT, Component::TYPE_TEXT_INPUT], true)) { - throw new \InvalidArgumentException('Component must be a StringSelect or TextInput.'); - } - $this->component = $component; return $this; From 333c5fcf6d1d28249681d17cc2f4e96c6bdc94b3 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 7 Aug 2025 09:03:55 -0400 Subject: [PATCH 14/19] Update SelectMenu.php --- src/Discord/Builders/Components/SelectMenu.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord/Builders/Components/SelectMenu.php b/src/Discord/Builders/Components/SelectMenu.php index 5606b61d2..759654caf 100644 --- a/src/Discord/Builders/Components/SelectMenu.php +++ b/src/Discord/Builders/Components/SelectMenu.php @@ -35,7 +35,7 @@ */ abstract class SelectMenu extends Interactive { - public const USAGE = ['Message', 'Modal']; + public const USAGE = ['Message']; /** * Component type. From 4ca0468fd971821a9d93d098077c5e2576f0b433 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 7 Aug 2025 09:03:57 -0400 Subject: [PATCH 15/19] Update ActionRow.php --- src/Discord/Builders/Components/ActionRow.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord/Builders/Components/ActionRow.php b/src/Discord/Builders/Components/ActionRow.php index 3a740f9c1..957486d61 100644 --- a/src/Discord/Builders/Components/ActionRow.php +++ b/src/Discord/Builders/Components/ActionRow.php @@ -24,6 +24,7 @@ */ class ActionRow extends Layout { + /** Usage of ActionRow in Modal is deprecated. Use `Component::Label` as the top-level container. */ public const USAGE = ['Message', 'Modal']; /** From 15da3574b9b3d440c4608bdb94de09840e1a1aa4 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 7 Aug 2025 09:05:45 -0400 Subject: [PATCH 16/19] Move setRequired to StringSelect It's only makes sense to restrict the usage to this context. --- src/Discord/Builders/Components/SelectMenu.php | 14 -------------- src/Discord/Builders/Components/StringSelect.php | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Discord/Builders/Components/SelectMenu.php b/src/Discord/Builders/Components/SelectMenu.php index 759654caf..59da15318 100644 --- a/src/Discord/Builders/Components/SelectMenu.php +++ b/src/Discord/Builders/Components/SelectMenu.php @@ -316,20 +316,6 @@ public function setDisabled(bool $disabled = true): self return $this; } - /** - * Sets whether the select menu is required. (Modal only). - * - * @param bool|null $required - * - * @return $this - */ - public function setRequired(?bool $required = false): self - { - $this->required = $required; - - return $this; - } - /** * Sets the callable listener for the select menu. The `$callback` function * will be called when the selection of the menu is changed. diff --git a/src/Discord/Builders/Components/StringSelect.php b/src/Discord/Builders/Components/StringSelect.php index 712caa2a2..ddac1e5d5 100644 --- a/src/Discord/Builders/Components/StringSelect.php +++ b/src/Discord/Builders/Components/StringSelect.php @@ -39,6 +39,20 @@ class StringSelect extends SelectMenu */ protected $options = []; + /** + * Sets whether the select menu is required. (Modal only). + * + * @param bool|null $required + * + * @return $this + */ + public function setRequired(?bool $required = false): self + { + $this->required = $required; + + return $this; + } + /** * Adds an option to the select menu. Maximum 25 options. * From e76e72a60ab9844e58363b2fd46cc834fafaa708 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 7 Aug 2025 09:11:59 -0400 Subject: [PATCH 17/19] Label length must be between 1 and 100 characters --- src/Discord/Builders/Components/Label.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Discord/Builders/Components/Label.php b/src/Discord/Builders/Components/Label.php index 5b07d2f4c..223079ff6 100644 --- a/src/Discord/Builders/Components/Label.php +++ b/src/Discord/Builders/Components/Label.php @@ -13,6 +13,8 @@ namespace Discord\Builders\Components; +use function Discord\poly_strlen; + /** * A Label is a top-level component. * @@ -25,7 +27,7 @@ * @since 10.19.0 * * @property int $type 18 for label component. - * @property string $label The text for the label. + * @property string $label The text for the label. Must be between 1 and 100 characters. * @property string|null $description Optional description for the label. * @property StringSelect|TextInput $component The component associated with the label. */ @@ -84,12 +86,16 @@ public static function new(string $label, $component, ?string $description = nul /** * Sets the label text. * - * @param string $label The text for the label. + * @param string $label The text for the label. Must be between 1 and 100 characters. * * @return self */ public function setLabel(string $label): self { + if (poly_strlen($label) === 0 || poly_strlen($label) > 100) { + throw new \LengthException('Label must be between 1 and 100 in length.'); + } + $this->label = $label; return $this; From 6f8ed25ff45f683255166754cf8c53bed1f643be Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 7 Aug 2025 09:27:13 -0400 Subject: [PATCH 18/19] Add Label support to Interaction::createListener --- src/Discord/Parts/Interactions/Interaction.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Discord/Parts/Interactions/Interaction.php b/src/Discord/Parts/Interactions/Interaction.php index bcab46f9c..f2660198d 100644 --- a/src/Discord/Parts/Interactions/Interaction.php +++ b/src/Discord/Parts/Interactions/Interaction.php @@ -69,7 +69,7 @@ class Interaction extends Part { /** - * {@inheritDoc} + * @inheritDoc */ protected $fillable = [ 'id', @@ -700,9 +700,11 @@ protected function createListener(string $custom_id, callable $submit, int|float $listener = function (Interaction $interaction) use ($custom_id, $submit, &$listener, &$timer) { if ($interaction->type == self::TYPE_MODAL_SUBMIT && $interaction->data->custom_id == $custom_id) { $components = Collection::for(RequestComponent::class, 'custom_id'); - foreach ($interaction->data->components as $actionrow) { - if ($actionrow->type == Component::TYPE_ACTION_ROW) { - foreach ($actionrow->components as $component) { + foreach ($interaction->data->components as $container) { + if ($container->type == Component::TYPE_LABEL) { + $components->pushItem($container->component); + } elseif ($container->type == Component::TYPE_ACTION_ROW) { + foreach ($container->components as $component) { $components->pushItem($component); } } From c0b079e4cab240b883848faeb9356c3017d8da2b Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 14 Aug 2025 07:50:41 -0400 Subject: [PATCH 19/19] Phase 1 updates --- src/Discord/Builders/Components/Label.php | 20 +++++++++++++------ .../Builders/Components/SelectMenu.php | 11 ++++++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Discord/Builders/Components/Label.php b/src/Discord/Builders/Components/Label.php index 223079ff6..2ce121ad5 100644 --- a/src/Discord/Builders/Components/Label.php +++ b/src/Discord/Builders/Components/Label.php @@ -27,8 +27,8 @@ * @since 10.19.0 * * @property int $type 18 for label component. - * @property string $label The text for the label. Must be between 1 and 100 characters. - * @property string|null $description Optional description for the label. + * @property string $label The text for the label. Must be between 1 and 45 characters. + * @property string|null $description Optional description for the label. Max 100 characters. * @property StringSelect|TextInput $component The component associated with the label. */ class Label extends ComponentObject @@ -86,14 +86,14 @@ public static function new(string $label, $component, ?string $description = nul /** * Sets the label text. * - * @param string $label The text for the label. Must be between 1 and 100 characters. + * @param string $label The text for the label. Must be between 1 and 45 characters. * * @return self */ public function setLabel(string $label): self { - if (poly_strlen($label) === 0 || poly_strlen($label) > 100) { - throw new \LengthException('Label must be between 1 and 100 in length.'); + if (poly_strlen($label) === 0 || poly_strlen($label) > 45) { + throw new \LengthException('Label must be between 1 and 45 in length.'); } $this->label = $label; @@ -104,12 +104,20 @@ public function setLabel(string $label): self /** * Sets the description text. * - * @param string|null $description The description for the label. + * @param string|null $description The description for the label. Max 100 characters. * * @return self */ public function setDescription(?string $description = null): self { + if (poly_strlen($description) === 0) { + $description = null; + } + + if (poly_strlen($description) > 100) { + throw new \LengthException('Description must be between 0 and 100 in length.'); + } + $this->description = $description; return $this; diff --git a/src/Discord/Builders/Components/SelectMenu.php b/src/Discord/Builders/Components/SelectMenu.php index 59da15318..6ad19494e 100644 --- a/src/Discord/Builders/Components/SelectMenu.php +++ b/src/Discord/Builders/Components/SelectMenu.php @@ -104,7 +104,7 @@ abstract class SelectMenu extends Interactive protected $disabled; /** - * Whether the select menu is required. (Modal only). + * Whether the select menu is required. Defaults to true. (Modal only). * * @var bool|null */ @@ -303,7 +303,7 @@ public function setMaxValues(?int $max_values): self } /** - * Sets the select menus disabled state. + * Sets the select menus disabled state. (Message only) * * @param bool $disabled * @@ -539,7 +539,7 @@ public function jsonSerialize(): array if (isset($this->min_values)) { if (isset($this->options) && $this->min_values > count($this->options)) { - throw new \OutOfBoundsException('There are less options than the minimum number of options to be selected.'); + throw new \DomainException('There are less options than the minimum number of options to be selected.'); } $content['min_values'] = $this->min_values; @@ -547,7 +547,7 @@ public function jsonSerialize(): array if (isset($this->max_values) && $this->max_values) { if (isset($this->options) && $this->max_values > count($this->options)) { - throw new \OutOfBoundsException('There are less options than the maximum number of options to be selected.'); + throw new \DomainException('There are less options than the maximum number of options to be selected.'); } $content['max_values'] = $this->max_values; @@ -559,6 +559,9 @@ public function jsonSerialize(): array if (isset($this->required)) { $content['required'] = true; + if ($this->min_values === null || $this->min_values === 0) { + throw new \LengthException('Required select menus must have a minimum value greater than 0.'); + } } return $content;