mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-08-18 09:00:48 +00:00
Add support for label components and select in modals
This commit is contained in:
parent
9fb74fd7a1
commit
3fb627d078
@ -70,6 +70,7 @@ if TYPE_CHECKING:
|
|||||||
ThumbnailComponent as ThumbnailComponentPayload,
|
ThumbnailComponent as ThumbnailComponentPayload,
|
||||||
ContainerComponent as ContainerComponentPayload,
|
ContainerComponent as ContainerComponentPayload,
|
||||||
UnfurledMediaItem as UnfurledMediaItemPayload,
|
UnfurledMediaItem as UnfurledMediaItemPayload,
|
||||||
|
LabelComponent as LabelComponentPayload,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .emoji import Emoji
|
from .emoji import Emoji
|
||||||
@ -109,6 +110,7 @@ __all__ = (
|
|||||||
'Container',
|
'Container',
|
||||||
'TextDisplay',
|
'TextDisplay',
|
||||||
'SeparatorComponent',
|
'SeparatorComponent',
|
||||||
|
'LabelComponent',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -348,6 +350,10 @@ class SelectMenu(Component):
|
|||||||
id: Optional[:class:`int`]
|
id: Optional[:class:`int`]
|
||||||
The ID of this component.
|
The ID of this component.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6
|
||||||
|
required: :class:`bool`
|
||||||
|
Whether the select is required. Only applicable within modals.
|
||||||
|
|
||||||
.. versionadded:: 2.6
|
.. versionadded:: 2.6
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -361,6 +367,7 @@ class SelectMenu(Component):
|
|||||||
'disabled',
|
'disabled',
|
||||||
'channel_types',
|
'channel_types',
|
||||||
'default_values',
|
'default_values',
|
||||||
|
'required',
|
||||||
'id',
|
'id',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -372,6 +379,7 @@ class SelectMenu(Component):
|
|||||||
self.placeholder: Optional[str] = data.get('placeholder')
|
self.placeholder: Optional[str] = data.get('placeholder')
|
||||||
self.min_values: int = data.get('min_values', 1)
|
self.min_values: int = data.get('min_values', 1)
|
||||||
self.max_values: int = data.get('max_values', 1)
|
self.max_values: int = data.get('max_values', 1)
|
||||||
|
self.required: bool = data.get('required', False)
|
||||||
self.options: List[SelectOption] = [SelectOption.from_dict(option) for option in data.get('options', [])]
|
self.options: List[SelectOption] = [SelectOption.from_dict(option) for option in data.get('options', [])]
|
||||||
self.disabled: bool = data.get('disabled', False)
|
self.disabled: bool = data.get('disabled', False)
|
||||||
self.channel_types: List[ChannelType] = [try_enum(ChannelType, t) for t in data.get('channel_types', [])]
|
self.channel_types: List[ChannelType] = [try_enum(ChannelType, t) for t in data.get('channel_types', [])]
|
||||||
@ -544,7 +552,7 @@ class TextInput(Component):
|
|||||||
------------
|
------------
|
||||||
custom_id: Optional[:class:`str`]
|
custom_id: Optional[:class:`str`]
|
||||||
The ID of the text input that gets received during an interaction.
|
The ID of the text input that gets received during an interaction.
|
||||||
label: :class:`str`
|
label: Optional[:class:`str`]
|
||||||
The label to display above the text input.
|
The label to display above the text input.
|
||||||
style: :class:`TextStyle`
|
style: :class:`TextStyle`
|
||||||
The style of the text input.
|
The style of the text input.
|
||||||
@ -580,7 +588,7 @@ class TextInput(Component):
|
|||||||
|
|
||||||
def __init__(self, data: TextInputPayload, /) -> None:
|
def __init__(self, data: TextInputPayload, /) -> None:
|
||||||
self.style: TextStyle = try_enum(TextStyle, data['style'])
|
self.style: TextStyle = try_enum(TextStyle, data['style'])
|
||||||
self.label: str = data['label']
|
self.label: Optional[str] = data.get('label')
|
||||||
self.custom_id: str = data['custom_id']
|
self.custom_id: str = data['custom_id']
|
||||||
self.placeholder: Optional[str] = data.get('placeholder')
|
self.placeholder: Optional[str] = data.get('placeholder')
|
||||||
self.value: Optional[str] = data.get('value')
|
self.value: Optional[str] = data.get('value')
|
||||||
@ -1309,6 +1317,62 @@ class Container(Component):
|
|||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
class LabelComponent(Component):
|
||||||
|
"""Represents a label component from the Discord Bot UI Kit.
|
||||||
|
|
||||||
|
This inherits from :class:`Component`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The user constructible and usable type for creating a label is
|
||||||
|
:class:`discord.ui.Label` not this one.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
label: :class:`str`
|
||||||
|
The label text to display.
|
||||||
|
description: Optional[:class:`str`]
|
||||||
|
The description text to display below the label, if any.
|
||||||
|
component: :class:`Component`
|
||||||
|
The component that this label is associated with.
|
||||||
|
id: Optional[:class:`int`]
|
||||||
|
The ID of this component.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
'label',
|
||||||
|
'description',
|
||||||
|
'commponent',
|
||||||
|
'id',
|
||||||
|
)
|
||||||
|
|
||||||
|
__repr_info__ = ('label', 'description', 'commponent', 'id,')
|
||||||
|
|
||||||
|
def __init__(self, data: LabelComponentPayload, state: Optional[ConnectionState]) -> None:
|
||||||
|
self.component: Component = _component_factory(data['component'], state) # type: ignore
|
||||||
|
self.label: str = data['label']
|
||||||
|
self.id: Optional[int] = data.get('id')
|
||||||
|
self.description: Optional[str] = data.get('description')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> Literal[ComponentType.label]:
|
||||||
|
return ComponentType.label
|
||||||
|
|
||||||
|
def to_dict(self) -> LabelComponentPayload:
|
||||||
|
payload: LabelComponentPayload = {
|
||||||
|
'type': self.type.value,
|
||||||
|
'label': self.label,
|
||||||
|
'component': self.component.to_dict(), # type: ignore
|
||||||
|
}
|
||||||
|
if self.description:
|
||||||
|
payload['description'] = self.description
|
||||||
|
if self.id is not None:
|
||||||
|
payload['id'] = self.id
|
||||||
|
return payload
|
||||||
|
|
||||||
|
|
||||||
def _component_factory(data: ComponentPayload, state: Optional[ConnectionState] = None) -> Optional[Component]:
|
def _component_factory(data: ComponentPayload, state: Optional[ConnectionState] = None) -> Optional[Component]:
|
||||||
if data['type'] == 1:
|
if data['type'] == 1:
|
||||||
return ActionRow(data)
|
return ActionRow(data)
|
||||||
@ -1332,3 +1396,5 @@ def _component_factory(data: ComponentPayload, state: Optional[ConnectionState]
|
|||||||
return SeparatorComponent(data)
|
return SeparatorComponent(data)
|
||||||
elif data['type'] == 17:
|
elif data['type'] == 17:
|
||||||
return Container(data, state)
|
return Container(data, state)
|
||||||
|
elif data['type'] == 18:
|
||||||
|
return LabelComponent(data, state)
|
||||||
|
@ -677,6 +677,7 @@ class ComponentType(Enum):
|
|||||||
file = 13
|
file = 13
|
||||||
separator = 14
|
separator = 14
|
||||||
container = 17
|
container = 17
|
||||||
|
label = 18
|
||||||
|
|
||||||
def __int__(self) -> int:
|
def __int__(self) -> int:
|
||||||
return self.value
|
return self.value
|
||||||
|
@ -30,7 +30,7 @@ from typing_extensions import NotRequired
|
|||||||
from .emoji import PartialEmoji
|
from .emoji import PartialEmoji
|
||||||
from .channel import ChannelType
|
from .channel import ChannelType
|
||||||
|
|
||||||
ComponentType = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17]
|
ComponentType = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18]
|
||||||
ButtonStyle = Literal[1, 2, 3, 4, 5, 6]
|
ButtonStyle = Literal[1, 2, 3, 4, 5, 6]
|
||||||
TextStyle = Literal[1, 2]
|
TextStyle = Literal[1, 2]
|
||||||
DefaultValueType = Literal['user', 'role', 'channel']
|
DefaultValueType = Literal['user', 'role', 'channel']
|
||||||
@ -110,7 +110,7 @@ class TextInput(ComponentBase):
|
|||||||
type: Literal[4]
|
type: Literal[4]
|
||||||
custom_id: str
|
custom_id: str
|
||||||
style: TextStyle
|
style: TextStyle
|
||||||
label: str
|
label: Optional[str]
|
||||||
placeholder: NotRequired[str]
|
placeholder: NotRequired[str]
|
||||||
value: NotRequired[str]
|
value: NotRequired[str]
|
||||||
required: NotRequired[bool]
|
required: NotRequired[bool]
|
||||||
@ -120,6 +120,7 @@ class TextInput(ComponentBase):
|
|||||||
|
|
||||||
class SelectMenu(SelectComponent):
|
class SelectMenu(SelectComponent):
|
||||||
type: Literal[3, 5, 6, 7, 8]
|
type: Literal[3, 5, 6, 7, 8]
|
||||||
|
required: NotRequired[bool] # Only for StringSelect within modals
|
||||||
options: NotRequired[List[SelectOption]]
|
options: NotRequired[List[SelectOption]]
|
||||||
channel_types: NotRequired[List[ChannelType]]
|
channel_types: NotRequired[List[ChannelType]]
|
||||||
default_values: NotRequired[List[SelectDefaultValues]]
|
default_values: NotRequired[List[SelectDefaultValues]]
|
||||||
@ -187,6 +188,13 @@ class ContainerComponent(ComponentBase):
|
|||||||
components: List[ContainerChildComponent]
|
components: List[ContainerChildComponent]
|
||||||
|
|
||||||
|
|
||||||
|
class LabelComponent(ComponentBase):
|
||||||
|
type: Literal[18]
|
||||||
|
label: str
|
||||||
|
description: NotRequired[str]
|
||||||
|
component: Union[StringSelectComponent, TextInput]
|
||||||
|
|
||||||
|
|
||||||
ActionRowChildComponent = Union[ButtonComponent, SelectMenu, TextInput]
|
ActionRowChildComponent = Union[ButtonComponent, SelectMenu, TextInput]
|
||||||
ContainerChildComponent = Union[
|
ContainerChildComponent = Union[
|
||||||
ActionRow,
|
ActionRow,
|
||||||
@ -199,4 +207,4 @@ ContainerChildComponent = Union[
|
|||||||
SeparatorComponent,
|
SeparatorComponent,
|
||||||
ThumbnailComponent,
|
ThumbnailComponent,
|
||||||
]
|
]
|
||||||
Component = Union[ActionRowChildComponent, ContainerChildComponent]
|
Component = Union[ActionRowChildComponent, LabelComponent, ContainerChildComponent]
|
||||||
|
@ -209,7 +209,13 @@ class ModalSubmitTextInputInteractionData(TypedDict):
|
|||||||
value: str
|
value: str
|
||||||
|
|
||||||
|
|
||||||
ModalSubmitComponentItemInteractionData = ModalSubmitTextInputInteractionData
|
class ModalSubmitStringSelectInteractionData(TypedDict):
|
||||||
|
type: Literal[3]
|
||||||
|
custom_id: str
|
||||||
|
values: List[str]
|
||||||
|
|
||||||
|
|
||||||
|
ModalSubmitComponentItemInteractionData = Union[ModalSubmitTextInputInteractionData, ModalSubmitStringSelectInteractionData]
|
||||||
|
|
||||||
|
|
||||||
class ModalSubmitActionRowInteractionData(TypedDict):
|
class ModalSubmitActionRowInteractionData(TypedDict):
|
||||||
@ -217,7 +223,14 @@ class ModalSubmitActionRowInteractionData(TypedDict):
|
|||||||
components: List[ModalSubmitComponentItemInteractionData]
|
components: List[ModalSubmitComponentItemInteractionData]
|
||||||
|
|
||||||
|
|
||||||
ModalSubmitComponentInteractionData = Union[ModalSubmitActionRowInteractionData, ModalSubmitComponentItemInteractionData]
|
class ModalSubmitLabelInteractionData(TypedDict):
|
||||||
|
type: Literal[18]
|
||||||
|
component: ModalSubmitComponentItemInteractionData
|
||||||
|
|
||||||
|
|
||||||
|
ModalSubmitComponentInteractionData = Union[
|
||||||
|
ModalSubmitLabelInteractionData, ModalSubmitActionRowInteractionData, ModalSubmitComponentItemInteractionData
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class ModalSubmitInteractionData(TypedDict):
|
class ModalSubmitInteractionData(TypedDict):
|
||||||
|
@ -24,3 +24,4 @@ from .separator import *
|
|||||||
from .text_display import *
|
from .text_display import *
|
||||||
from .thumbnail import *
|
from .thumbnail import *
|
||||||
from .action_row import *
|
from .action_row import *
|
||||||
|
from .label import *
|
||||||
|
140
discord/ui/label.py
Normal file
140
discord/ui/label.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
"""
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-present Rapptz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, Generator, Literal, Optional, Tuple, TypeVar
|
||||||
|
|
||||||
|
from ..components import LabelComponent
|
||||||
|
from ..enums import ComponentType
|
||||||
|
from ..utils import MISSING
|
||||||
|
from .item import Item
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing_extensions import Self
|
||||||
|
|
||||||
|
from ..types.components import LabelComponent as LabelComponentPayload
|
||||||
|
from .view import View
|
||||||
|
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
__all__ = (
|
||||||
|
'Label',
|
||||||
|
)
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
V = TypeVar('V', bound='View', covariant=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Label(Item[V]):
|
||||||
|
"""Represents a UI label within a modal.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
text: :class:`str`
|
||||||
|
The text to display above the input field.
|
||||||
|
Can only be up to 45 characters.
|
||||||
|
description: Optional[:class:`str`]
|
||||||
|
The description text to display right below the label text.
|
||||||
|
Can only be up to 100 characters.
|
||||||
|
component: Union[:class:`discord.ui.TextInput`, :class:`discord.ui.Select`]
|
||||||
|
The component to display below the label.
|
||||||
|
id: Optional[:class:`int`]
|
||||||
|
The ID of the component. This must be unique across the view.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
------------
|
||||||
|
text: :class:`str`
|
||||||
|
The text to display above the input field.
|
||||||
|
Can only be up to 45 characters.
|
||||||
|
description: Optional[:class:`str`]
|
||||||
|
The description text to display right below the label text.
|
||||||
|
Can only be up to 100 characters.
|
||||||
|
component: :class:`Item`
|
||||||
|
The component to display below the label. Currently only
|
||||||
|
supports :class:`TextInput` and :class:`Select`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__item_repr_attributes__: Tuple[str, ...] = (
|
||||||
|
'text',
|
||||||
|
'description',
|
||||||
|
'component',
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
text: str,
|
||||||
|
component: Item[V],
|
||||||
|
description: Optional[str] = None,
|
||||||
|
id: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.component: Item[V] = component
|
||||||
|
self.text: str = text
|
||||||
|
self.description: Optional[str] = description
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def width(self) -> int:
|
||||||
|
return 5
|
||||||
|
|
||||||
|
def _has_children(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def walk_children(self) -> Generator[Item[V], None, None]:
|
||||||
|
yield self.component
|
||||||
|
|
||||||
|
def to_component_dict(self) -> LabelComponentPayload:
|
||||||
|
payload: LabelComponentPayload = {
|
||||||
|
'type': ComponentType.label.value,
|
||||||
|
'label': self.text,
|
||||||
|
'component': self.component.to_component_dict(), # type: ignore
|
||||||
|
}
|
||||||
|
if self.description:
|
||||||
|
payload['description'] = self.description
|
||||||
|
if self.id is not None:
|
||||||
|
payload['id'] = self.id
|
||||||
|
return payload
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_component(cls, component: LabelComponent) -> Self:
|
||||||
|
from .view import _component_to_item
|
||||||
|
|
||||||
|
self = cls(
|
||||||
|
text=component.label,
|
||||||
|
component=MISSING,
|
||||||
|
description=component.description,
|
||||||
|
)
|
||||||
|
self.component = _component_to_item(component.component, self)
|
||||||
|
return self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> Literal[ComponentType.label]:
|
||||||
|
return ComponentType.label
|
||||||
|
|
||||||
|
def is_dispatchable(self) -> bool:
|
||||||
|
return False
|
@ -34,6 +34,7 @@ from ..utils import MISSING, find
|
|||||||
from .._types import ClientT
|
from .._types import ClientT
|
||||||
from .item import Item
|
from .item import Item
|
||||||
from .view import View
|
from .view import View
|
||||||
|
from .label import Label
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
@ -170,8 +171,10 @@ class Modal(View):
|
|||||||
for component in components:
|
for component in components:
|
||||||
if component['type'] == 1:
|
if component['type'] == 1:
|
||||||
self._refresh(interaction, component['components'])
|
self._refresh(interaction, component['components'])
|
||||||
|
elif component['type'] == 18:
|
||||||
|
self._refresh(interaction, [component['component']])
|
||||||
else:
|
else:
|
||||||
item = find(lambda i: i.custom_id == component['custom_id'], self._children) # type: ignore
|
item = find(lambda i: getattr(i, 'custom_id', None) == component['custom_id'], self.walk_children()) # type: ignore
|
||||||
if item is None:
|
if item is None:
|
||||||
_log.debug("Modal interaction referencing unknown item custom_id %s. Discarding", component['custom_id'])
|
_log.debug("Modal interaction referencing unknown item custom_id %s. Discarding", component['custom_id'])
|
||||||
continue
|
continue
|
||||||
@ -194,6 +197,28 @@ class Modal(View):
|
|||||||
# In the future, maybe this will require checking if we set an error response.
|
# In the future, maybe this will require checking if we set an error response.
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
|
def to_components(self) -> List[Dict[str, Any]]:
|
||||||
|
def key(item: Item) -> int:
|
||||||
|
return item._rendered_row or 0
|
||||||
|
|
||||||
|
children = sorted(self._children, key=key)
|
||||||
|
components: List[Dict[str, Any]] = []
|
||||||
|
for child in children:
|
||||||
|
if isinstance(child, Label):
|
||||||
|
components.append(child.to_component_dict()) # type: ignore
|
||||||
|
else:
|
||||||
|
# Every implicit child wrapped in an ActionRow in a modal
|
||||||
|
# has a single child of width 5
|
||||||
|
# It's also deprecated to use ActionRow in modals
|
||||||
|
components.append(
|
||||||
|
{
|
||||||
|
'type': 1,
|
||||||
|
'components': [child.to_component_dict()],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return components
|
||||||
|
|
||||||
def _dispatch_submit(
|
def _dispatch_submit(
|
||||||
self, interaction: Interaction, components: List[ModalSubmitComponentInteractionDataPayload]
|
self, interaction: Interaction, components: List[ModalSubmitComponentInteractionDataPayload]
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -239,6 +239,7 @@ class BaseSelect(Item[V]):
|
|||||||
min_values: Optional[int] = None,
|
min_values: Optional[int] = None,
|
||||||
max_values: Optional[int] = None,
|
max_values: Optional[int] = None,
|
||||||
disabled: bool = False,
|
disabled: bool = False,
|
||||||
|
required: bool = False,
|
||||||
options: List[SelectOption] = MISSING,
|
options: List[SelectOption] = MISSING,
|
||||||
channel_types: List[ChannelType] = MISSING,
|
channel_types: List[ChannelType] = MISSING,
|
||||||
default_values: Sequence[SelectDefaultValue] = MISSING,
|
default_values: Sequence[SelectDefaultValue] = MISSING,
|
||||||
@ -257,6 +258,7 @@ class BaseSelect(Item[V]):
|
|||||||
min_values=min_values,
|
min_values=min_values,
|
||||||
max_values=max_values,
|
max_values=max_values,
|
||||||
disabled=disabled,
|
disabled=disabled,
|
||||||
|
required=required,
|
||||||
channel_types=[] if channel_types is MISSING else channel_types,
|
channel_types=[] if channel_types is MISSING else channel_types,
|
||||||
options=[] if options is MISSING else options,
|
options=[] if options is MISSING else options,
|
||||||
default_values=[] if default_values is MISSING else default_values,
|
default_values=[] if default_values is MISSING else default_values,
|
||||||
@ -332,6 +334,18 @@ class BaseSelect(Item[V]):
|
|||||||
def disabled(self, value: bool) -> None:
|
def disabled(self, value: bool) -> None:
|
||||||
self._underlying.disabled = bool(value)
|
self._underlying.disabled = bool(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def required(self) -> bool:
|
||||||
|
""":class:`bool`: Whether the select is required or not. Only supported in modals.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6
|
||||||
|
"""
|
||||||
|
return self._underlying.required
|
||||||
|
|
||||||
|
@required.setter
|
||||||
|
def required(self, value: bool) -> None:
|
||||||
|
self._underlying.required = bool(value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def width(self) -> int:
|
def width(self) -> int:
|
||||||
return 5
|
return 5
|
||||||
@ -399,6 +413,10 @@ class Select(BaseSelect[V]):
|
|||||||
Can only contain up to 25 items.
|
Can only contain up to 25 items.
|
||||||
disabled: :class:`bool`
|
disabled: :class:`bool`
|
||||||
Whether the select is disabled or not.
|
Whether the select is disabled or not.
|
||||||
|
required: :class:`bool`
|
||||||
|
Whether the select is required. Only applicable within modals.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6
|
||||||
row: Optional[:class:`int`]
|
row: Optional[:class:`int`]
|
||||||
The relative row this select menu belongs to. A Discord component can only have 5
|
The relative row this select menu belongs to. A Discord component can only have 5
|
||||||
rows. By default, items are arranged automatically into those 5 rows. If you'd
|
rows. By default, items are arranged automatically into those 5 rows. If you'd
|
||||||
@ -426,6 +444,7 @@ class Select(BaseSelect[V]):
|
|||||||
max_values: int = 1,
|
max_values: int = 1,
|
||||||
options: List[SelectOption] = MISSING,
|
options: List[SelectOption] = MISSING,
|
||||||
disabled: bool = False,
|
disabled: bool = False,
|
||||||
|
required: bool = True,
|
||||||
row: Optional[int] = None,
|
row: Optional[int] = None,
|
||||||
id: Optional[int] = None,
|
id: Optional[int] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -436,6 +455,7 @@ class Select(BaseSelect[V]):
|
|||||||
min_values=min_values,
|
min_values=min_values,
|
||||||
max_values=max_values,
|
max_values=max_values,
|
||||||
disabled=disabled,
|
disabled=disabled,
|
||||||
|
required=required,
|
||||||
options=options,
|
options=options,
|
||||||
row=row,
|
row=row,
|
||||||
id=id,
|
id=id,
|
||||||
|
@ -29,7 +29,7 @@ from typing import TYPE_CHECKING, Literal, Optional, Tuple, TypeVar
|
|||||||
|
|
||||||
from ..components import TextInput as TextInputComponent
|
from ..components import TextInput as TextInputComponent
|
||||||
from ..enums import ComponentType, TextStyle
|
from ..enums import ComponentType, TextStyle
|
||||||
from ..utils import MISSING
|
from ..utils import MISSING, deprecated
|
||||||
from .item import Item
|
from .item import Item
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -63,9 +63,15 @@ class TextInput(Item[V]):
|
|||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
------------
|
------------
|
||||||
label: :class:`str`
|
label: Optional[:class:`str`]
|
||||||
The label to display above the text input.
|
The label to display above the text input.
|
||||||
Can only be up to 45 characters.
|
Can only be up to 45 characters.
|
||||||
|
|
||||||
|
.. deprecated:: 2.6
|
||||||
|
This parameter is deprecated, use :class:`discord.ui.Label` instead.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.6
|
||||||
|
This parameter is now optional and defaults to ``None``.
|
||||||
custom_id: :class:`str`
|
custom_id: :class:`str`
|
||||||
The ID of the text input that gets received during an interaction.
|
The ID of the text input that gets received during an interaction.
|
||||||
If not given then one is generated for you.
|
If not given then one is generated for you.
|
||||||
@ -108,7 +114,7 @@ class TextInput(Item[V]):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
label: str,
|
label: Optional[str] = None,
|
||||||
style: TextStyle = TextStyle.short,
|
style: TextStyle = TextStyle.short,
|
||||||
custom_id: str = MISSING,
|
custom_id: str = MISSING,
|
||||||
placeholder: Optional[str] = None,
|
placeholder: Optional[str] = None,
|
||||||
@ -166,12 +172,14 @@ class TextInput(Item[V]):
|
|||||||
return self._value or ''
|
return self._value or ''
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def label(self) -> str:
|
@deprecated('discord.ui.Label')
|
||||||
|
def label(self) -> Optional[str]:
|
||||||
""":class:`str`: The label of the text input."""
|
""":class:`str`: The label of the text input."""
|
||||||
return self._underlying.label
|
return self._underlying.label
|
||||||
|
|
||||||
@label.setter
|
@label.setter
|
||||||
def label(self, value: str) -> None:
|
@deprecated('discord.ui.Label')
|
||||||
|
def label(self, value: Optional[str]) -> None:
|
||||||
self._underlying.label = value
|
self._underlying.label = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -65,6 +65,7 @@ from ..components import (
|
|||||||
SeparatorComponent,
|
SeparatorComponent,
|
||||||
ThumbnailComponent,
|
ThumbnailComponent,
|
||||||
Container as ContainerComponent,
|
Container as ContainerComponent,
|
||||||
|
LabelComponent,
|
||||||
)
|
)
|
||||||
from ..utils import get as _utils_get, find as _utils_find
|
from ..utils import get as _utils_get, find as _utils_find
|
||||||
|
|
||||||
@ -147,6 +148,10 @@ def _component_to_item(component: Component, parent: Optional[Item] = None) -> I
|
|||||||
from .container import Container
|
from .container import Container
|
||||||
|
|
||||||
item = Container.from_component(component)
|
item = Container.from_component(component)
|
||||||
|
elif isinstance(component, LabelComponent):
|
||||||
|
from .label import Label
|
||||||
|
|
||||||
|
item = Label.from_component(component)
|
||||||
else:
|
else:
|
||||||
item = Item.from_component(component)
|
item = Item.from_component(component)
|
||||||
|
|
||||||
|
@ -113,6 +113,15 @@ TextInput
|
|||||||
:members:
|
:members:
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
|
|
||||||
|
LabelComponent
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. attributetable:: LabelComponent
|
||||||
|
|
||||||
|
.. autoclass:: LabelComponent()
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
|
||||||
SectionComponent
|
SectionComponent
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
@ -425,7 +434,7 @@ Enumerations
|
|||||||
.. attribute:: media_gallery
|
.. attribute:: media_gallery
|
||||||
|
|
||||||
Represents a media gallery component.
|
Represents a media gallery component.
|
||||||
|
|
||||||
.. versionadded:: 2.6
|
.. versionadded:: 2.6
|
||||||
|
|
||||||
.. attribute:: file
|
.. attribute:: file
|
||||||
@ -446,6 +455,12 @@ Enumerations
|
|||||||
|
|
||||||
.. versionadded:: 2.6
|
.. versionadded:: 2.6
|
||||||
|
|
||||||
|
.. attribute:: label
|
||||||
|
|
||||||
|
Represents a label container component, usually in a modal.
|
||||||
|
|
||||||
|
.. versionadded:: 2.6
|
||||||
|
|
||||||
.. class:: ButtonStyle
|
.. class:: ButtonStyle
|
||||||
|
|
||||||
Represents the style of the button component.
|
Represents the style of the button component.
|
||||||
@ -742,6 +757,15 @@ File
|
|||||||
:members:
|
:members:
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
|
|
||||||
|
Label
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
.. attributetable:: discord.ui.Label
|
||||||
|
|
||||||
|
.. autoclass:: discord.ui.Label
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
|
||||||
MediaGallery
|
MediaGallery
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
Loading…
x
Reference in New Issue
Block a user