66import time
77from functools import partial
88from itertools import groupby
9- from typing import TYPE_CHECKING , Any , Callable
9+ from typing import TYPE_CHECKING , Any , Callable , TypeVar
1010
1111from ..enums import ComponentType
1212from ..utils import find
1313from .input_text import InputText
14+ from .item import Item
1415from .select import Select
16+ from .text_display import TextDisplay
1517
1618__all__ = (
1719 "Modal" ,
2527 from ..interactions import Interaction
2628 from ..state import ConnectionState
2729
30+ M = TypeVar ("M" , bound = "Modal" , covariant = True )
31+
2832
2933class Modal :
3034 """Represents a UI Modal dialog.
@@ -35,12 +39,12 @@ class Modal:
3539
3640 .. versionchanged:: 2.7
3741
38- :attr :`discord.ComponentType.string_select ` can now be used in modals.
42+ :class :`discord.ui.Select` and :class:`discord.ui.TextDisplay ` can now be used in modals.
3943
4044 Parameters
4145 ----------
42- children: Union[:class:`InputText`, :class:`Select`]
43- The initial InputText or Select components that are displayed in the modal dialog.
46+ children: Union[:class:`InputText`, :class:`Select`, :class:`TextDisplay` ]
47+ The initial InputText, Select, or TextDisplay components that are displayed in the modal dialog.
4448 title: :class:`str`
4549 The title of the modal dialog.
4650 Must be 45 characters or fewer.
@@ -60,7 +64,7 @@ class Modal:
6064
6165 def __init__ (
6266 self ,
63- * children : InputText | Select ,
67+ * children : Item [ M ] ,
6468 title : str ,
6569 custom_id : str | None = None ,
6670 timeout : float | None = None ,
@@ -74,7 +78,7 @@ def __init__(
7478 if len (title ) > 45 :
7579 raise ValueError ("title must be 45 characters or fewer" )
7680 self ._title = title
77- self ._children : list [InputText | Select ] = list (children )
81+ self ._children : list [Item [ M ] ] = list (children )
7882 self ._weights = _ModalWeights (self ._children )
7983 loop = asyncio .get_running_loop ()
8084 self ._stopped : asyncio .Future [bool ] = loop .create_future ()
@@ -145,23 +149,18 @@ def title(self, value: str):
145149 self ._title = value
146150
147151 @property
148- def children (self ) -> list [InputText | Select ]:
152+ def children (self ) -> list [Item [ M ] ]:
149153 """The child components associated with the modal dialog."""
150154 return self ._children
151155
152156 @children .setter
153- def children (self , value : list [InputText | Select ]):
157+ def children (self , value : list [Item [ M ] ]):
154158 for item in value :
155- if not isinstance (item , (InputText , Select )):
159+ if not isinstance (item , (InputText , Select , TextDisplay )):
156160 raise TypeError (
157- "all Modal children must be InputText or Select , not"
161+ "all Modal children must be InputText, Select, or TextDisplay , not"
158162 f" { item .__class__ .__name__ } "
159163 )
160- elif (
161- isinstance (item , Select )
162- and item .type is not ComponentType .string_select
163- ):
164- raise TypeError ("only string selects may be added to modals" )
165164 self ._weights = _ModalWeights (self ._children )
166165 self ._children = value
167166
@@ -194,7 +193,7 @@ async def callback(self, interaction: Interaction):
194193 self .stop ()
195194
196195 def to_components (self ) -> list [dict [str , Any ]]:
197- def key (item : InputText | Select ) -> int :
196+ def key (item : Item [ M ] ) -> int :
198197 return item ._rendered_row or 0
199198
200199 children = sorted (self ._children , key = key )
@@ -231,35 +230,33 @@ def key(item: InputText | Select) -> int:
231230
232231 return components
233232
234- def add_item (self , item : InputText | Select ) -> Self :
235- """Adds an InputText or Select component to the modal dialog.
233+ def add_item (self , item : Item [ M ] ) -> Self :
234+ """Adds a component to the modal dialog.
236235
237236 Parameters
238237 ----------
239- item: Union[:class:`InputText`, :class:`Select `]
238+ item: Union[:class:`Item `]
240239 The item to add to the modal dialog
241240 """
242241
243242 if len (self ._children ) > 5 :
244243 raise ValueError ("You can only have up to 5 items in a modal dialog." )
245244
246- if not isinstance (item , (InputText , Select )):
247- raise TypeError (f"expected InputText or Select, not { item .__class__ !r} " )
248- if isinstance (item , Select ) and item .type is not ComponentType .string_select :
249- raise TypeError ("only string selects may be added to modals" )
250- if not item .label :
251- raise ValueError ("Item must have a label set" )
245+ if not isinstance (item , (InputText , Select , TextDisplay )):
246+ raise TypeError (f"expected InputText, Select, or TextDisplay, not { item .__class__ !r} " )
247+ if isinstance (item , (InputText , Select )) and not item .label :
248+ raise ValueError ("InputTexts and Selects must have a label set" )
252249
253250 self ._weights .add_item (item )
254251 self ._children .append (item )
255252 return self
256253
257- def remove_item (self , item : InputText | Select ) -> Self :
258- """Removes an InputText or Select component from the modal dialog.
254+ def remove_item (self , item : Item [ M ] ) -> Self :
255+ """Removes a component from the modal dialog.
259256
260257 Parameters
261258 ----------
262- item: Union[:class:`InputText`, :class:`Select `]
259+ item: Union[:class:`Item `]
263260 The item to remove from the modal dialog.
264261 """
265262 try :
@@ -268,7 +265,7 @@ def remove_item(self, item: InputText | Select) -> Self:
268265 pass
269266 return self
270267
271- def get_item (self , id : str | int ) -> Select | InputText | None :
268+ def get_item (self , id : str | int ) -> Item [ M ] | None :
272269 """Gets an item from the modal. Roughly equal to `utils.get(modal.children, ...)`.
273270 If an :class:`int` is provided, the item will be retrieved by ``id``, otherwise by ``custom_id``.
274271
@@ -333,7 +330,7 @@ async def on_timeout(self) -> None:
333330class _ModalWeights :
334331 __slots__ = ("weights" ,)
335332
336- def __init__ (self , children : list [InputText | Select ]):
333+ def __init__ (self , children : list [Item [ M ] ]):
337334 self .weights : list [int ] = [0 , 0 , 0 , 0 , 0 ]
338335
339336 key = lambda i : sys .maxsize if i .row is None else i .row
@@ -342,14 +339,14 @@ def __init__(self, children: list[InputText | Select]):
342339 for item in group :
343340 self .add_item (item )
344341
345- def find_open_space (self , item : InputText | Select ) -> int :
342+ def find_open_space (self , item : Item [ M ] ) -> int :
346343 for index , weight in enumerate (self .weights ):
347344 if weight + item .width <= 5 :
348345 return index
349346
350347 raise ValueError ("could not find open space for item" )
351348
352- def add_item (self , item : InputText | Select ) -> None :
349+ def add_item (self , item : Item [ M ] ) -> None :
353350 if item .row is not None :
354351 total = self .weights [item .row ] + item .width
355352 if total > 5 :
@@ -363,7 +360,7 @@ def add_item(self, item: InputText | Select) -> None:
363360 self .weights [index ] += item .width
364361 item ._rendered_row = index
365362
366- def remove_item (self , item : InputText | Select ) -> None :
363+ def remove_item (self , item : Item [ M ] ) -> None :
367364 if item ._rendered_row is not None :
368365 self .weights [item ._rendered_row ] -= item .width
369366 item ._rendered_row = None
0 commit comments