@@ -58,7 +58,7 @@ def __init__(self, ctx: commands.Context, *pages, **options):
5858 ">" : self .next_page ,
5959 ">>" : self .last_page ,
6060 }
61- self ._buttons_map = {"<<" : None , "<" : None , ">" : None , ">>" : None }
61+ self ._buttons_map = {k : None for k in self . callback_map . keys () }
6262
6363 async def show_page (self , index : int ) -> typing .Optional [typing .Dict ]:
6464 """
@@ -84,34 +84,19 @@ async def show_page(self, index: int) -> typing.Optional[typing.Dict]:
8484 self .update_disabled_status ()
8585 return result
8686
87- def update_disabled_status (self ):
88- if self .current == self .first_page ():
89- # disable << button
90- if self ._buttons_map ["<<" ] is not None :
91- self ._buttons_map ["<<" ].disabled = True
92-
93- if self ._buttons_map ["<" ] is not None :
94- self ._buttons_map ["<" ].disabled = True
95- else :
96- if self ._buttons_map ["<<" ] is not None :
97- self ._buttons_map ["<<" ].disabled = False
98-
99- if self ._buttons_map ["<" ] is not None :
100- self ._buttons_map ["<" ].disabled = False
101-
102- if self .current == self .last_page ():
103- # disable >> button
104- if self ._buttons_map [">>" ] is not None :
105- self ._buttons_map [">>" ].disabled = True
106-
107- if self ._buttons_map [">" ] is not None :
108- self ._buttons_map [">" ].disabled = True
109- else :
110- if self ._buttons_map [">>" ] is not None :
111- self ._buttons_map [">>" ].disabled = False
112-
113- if self ._buttons_map [">" ] is not None :
114- self ._buttons_map [">" ].disabled = False
87+ def update_disabled_status (self ) -> None :
88+ for label , button in self ._buttons_map .items ():
89+ if button is None :
90+ continue
91+ elif any (
92+ (
93+ self .current == self .first_page () and label in ("<<" , "<" ),
94+ self .current == self .last_page () and label in (">>" , ">" ),
95+ )
96+ ):
97+ button .disabled = True
98+ else :
99+ button .disabled = False
115100
116101 async def create_base (self , item ) -> None :
117102 """
@@ -227,7 +212,7 @@ def __init__(self, handler: PaginatorSession, *args, **kwargs):
227212 async def stop_button (self , interaction : Interaction , button : Button ):
228213 await self .handler .close (interaction = interaction )
229214
230- def fill_items (self ):
215+ def fill_items (self ) -> None :
231216 if self .handler .select_menu is not None :
232217 self .add_item (self .handler .select_menu )
233218
@@ -246,7 +231,7 @@ def fill_items(self):
246231 self .add_item (button )
247232 self .add_item (self .stop_button )
248233
249- async def interaction_check (self , interaction : Interaction ):
234+ async def interaction_check (self , interaction : Interaction ) -> bool :
250235 """Only allow the message author to interact"""
251236 if interaction .user != self .handler .ctx .author :
252237 await interaction .response .send_message (
@@ -280,34 +265,86 @@ def __init__(self, handler, page_callback, **kwargs):
280265 self .handler = handler
281266 self .page_callback = page_callback
282267
283- async def callback (self , interaction : Interaction ):
268+ async def callback (self , interaction : Interaction ) -> None :
284269 kwargs = await self .handler .show_page (self .page_callback ())
270+ select_menu = self .handler .select_menu
271+ if select_menu is not None :
272+ select_menu .update_options (True )
285273 await interaction .response .edit_message (** kwargs , view = self .view )
286274
287275
288276class PageSelect (Select ):
289277 def __init__ (self , handler : PaginatorSession , pages : typing .List [typing .Tuple [str ]]):
290278 self .handler = handler
291- options = []
279+ self . _all_options = [] # no limits
292280 for n , (label , description ) in enumerate (pages ):
293- options .append (discord .SelectOption (label = label , description = description , value = str (n )))
281+ self . _all_options .append (discord .SelectOption (label = label , description = description , value = str (n )))
294282
295- options = options [: 25 ] # max 25 options
283+ options = self . update_options ()
296284 super ().__init__ (placeholder = "Select a page" , min_values = 1 , max_values = 1 , options = options )
297285
298- async def callback (self , interaction : Interaction ):
286+ def update_options (self , refresh_options : bool = False ) -> typing .List [discord .SelectOption ]:
287+ """
288+ A helper to dynamically update the select menu options based on the current page.
289+ """
290+ current = self .handler .current
291+ differ = prev = after = 0
292+
293+ def max_reached ():
294+ return prev + after >= 25 # max select options
295+
296+ while not max_reached ():
297+ differ += 1
298+ inc_prev = current - differ >= 0
299+ inc_next = current + differ <= len (self ._all_options )
300+ if not any ((inc_prev , inc_next )):
301+ break
302+ if inc_prev and not max_reached ():
303+ prev += 1
304+ if inc_next and not max_reached ():
305+ after += 1
306+
307+ options = self ._all_options [current - prev : current + after ]
308+ if refresh_options :
309+ self .options .clear ()
310+ curr_option = self ._all_options [current ]
311+ for option in options :
312+ option .default = option == curr_option
313+ self .append_option (option )
314+ return options
315+
316+ async def callback (self , interaction : Interaction ) -> None :
299317 page = int (self .values [0 ])
300318 kwargs = await self .handler .show_page (page )
319+ self .update_options (True )
301320 await interaction .response .edit_message (** kwargs , view = self .view )
302321
303322
304323class EmbedPaginatorSession (PaginatorSession ):
305- def __init__ (self , ctx : commands .Context , * embeds , ** options ):
324+ """
325+ Class that interactively paginates embed pages.
326+ This inherits from PaginatorSession.
327+ Parameters
328+ ----------
329+ ctx : Context
330+ The context of the command.
331+ embeds : List[discord.Embed]
332+ A list of entries to paginate.
333+ create_select : bool
334+ Whether to create the select menu. Defaults to True.
335+ """
336+
337+ def __init__ (
338+ self ,
339+ ctx : commands .Context ,
340+ * embeds : typing .List [discord .Embed ],
341+ create_select : bool = True ,
342+ ** options ,
343+ ):
306344 super ().__init__ (ctx , * embeds , ** options )
307345
308346 if len (self .pages ) > 1 :
309347 select_options = []
310- create_select = True
311348 for i , embed in enumerate (self .pages ):
312349 footer_text = f"Page { i + 1 } of { len (self .pages )} "
313350 if embed .footer .text :
@@ -320,16 +357,22 @@ def __init__(self, ctx: commands.Context, *embeds, **options):
320357 embed .set_footer (text = footer_text , icon_url = icon_url )
321358
322359 # select menu
360+ if not create_select :
361+ continue
362+
323363 if embed .author .name :
324364 title = embed .author .name [:30 ].strip ()
325365 if len (embed .author .name ) > 30 :
326366 title += "..."
327- else :
367+ elif embed . title :
328368 title = embed .title [:30 ].strip ()
329369 if len (embed .title ) > 30 :
330370 title += "..."
331- if not title :
332- create_select = False
371+ else :
372+ title = None
373+
374+ if not title :
375+ create_select = False
333376
334377 if embed .description :
335378 description = embed .description [:40 ].replace ("*" , "" ).replace ("`" , "" ).strip ()
@@ -352,7 +395,7 @@ def add_page(self, item: Embed) -> None:
352395 async def _create_base (self , item : Embed , view : View ) -> None :
353396 self .base = await self .destination .send (embed = item , view = view )
354397
355- def _show_page (self , page ):
398+ def _show_page (self , page ) -> typing . Dict :
356399 return dict (embed = page )
357400
358401
@@ -368,7 +411,7 @@ def add_page(self, item: str) -> None:
368411 else :
369412 raise TypeError ("Page must be a str object." )
370413
371- def _set_footer (self ):
414+ def _set_footer (self ) -> None :
372415 if self .embed is not None :
373416 footer_text = f"Page { self .current + 1 } of { len (self .pages )} "
374417 if self .footer_text :
0 commit comments