@@ -58,7 +58,7 @@ def __init__(self, ctx: commands.Context, *pages, **options):
58
58
">" : self .next_page ,
59
59
">>" : self .last_page ,
60
60
}
61
- self ._buttons_map = {"<<" : None , "<" : None , ">" : None , ">>" : None }
61
+ self ._buttons_map = {k : None for k in self . callback_map . keys () }
62
62
63
63
async def show_page (self , index : int ) -> typing .Optional [typing .Dict ]:
64
64
"""
@@ -84,34 +84,19 @@ async def show_page(self, index: int) -> typing.Optional[typing.Dict]:
84
84
self .update_disabled_status ()
85
85
return result
86
86
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
115
100
116
101
async def create_base (self , item ) -> None :
117
102
"""
@@ -227,7 +212,7 @@ def __init__(self, handler: PaginatorSession, *args, **kwargs):
227
212
async def stop_button (self , interaction : Interaction , button : Button ):
228
213
await self .handler .close (interaction = interaction )
229
214
230
- def fill_items (self ):
215
+ def fill_items (self ) -> None :
231
216
if self .handler .select_menu is not None :
232
217
self .add_item (self .handler .select_menu )
233
218
@@ -246,7 +231,7 @@ def fill_items(self):
246
231
self .add_item (button )
247
232
self .add_item (self .stop_button )
248
233
249
- async def interaction_check (self , interaction : Interaction ):
234
+ async def interaction_check (self , interaction : Interaction ) -> bool :
250
235
"""Only allow the message author to interact"""
251
236
if interaction .user != self .handler .ctx .author :
252
237
await interaction .response .send_message (
@@ -280,34 +265,86 @@ def __init__(self, handler, page_callback, **kwargs):
280
265
self .handler = handler
281
266
self .page_callback = page_callback
282
267
283
- async def callback (self , interaction : Interaction ):
268
+ async def callback (self , interaction : Interaction ) -> None :
284
269
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 )
285
273
await interaction .response .edit_message (** kwargs , view = self .view )
286
274
287
275
288
276
class PageSelect (Select ):
289
277
def __init__ (self , handler : PaginatorSession , pages : typing .List [typing .Tuple [str ]]):
290
278
self .handler = handler
291
- options = []
279
+ self . _all_options = [] # no limits
292
280
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 )))
294
282
295
- options = options [: 25 ] # max 25 options
283
+ options = self . update_options ()
296
284
super ().__init__ (placeholder = "Select a page" , min_values = 1 , max_values = 1 , options = options )
297
285
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 :
299
317
page = int (self .values [0 ])
300
318
kwargs = await self .handler .show_page (page )
319
+ self .update_options (True )
301
320
await interaction .response .edit_message (** kwargs , view = self .view )
302
321
303
322
304
323
class 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
+ ):
306
344
super ().__init__ (ctx , * embeds , ** options )
307
345
308
346
if len (self .pages ) > 1 :
309
347
select_options = []
310
- create_select = True
311
348
for i , embed in enumerate (self .pages ):
312
349
footer_text = f"Page { i + 1 } of { len (self .pages )} "
313
350
if embed .footer .text :
@@ -320,16 +357,22 @@ def __init__(self, ctx: commands.Context, *embeds, **options):
320
357
embed .set_footer (text = footer_text , icon_url = icon_url )
321
358
322
359
# select menu
360
+ if not create_select :
361
+ continue
362
+
323
363
if embed .author .name :
324
364
title = embed .author .name [:30 ].strip ()
325
365
if len (embed .author .name ) > 30 :
326
366
title += "..."
327
- else :
367
+ elif embed . title :
328
368
title = embed .title [:30 ].strip ()
329
369
if len (embed .title ) > 30 :
330
370
title += "..."
331
- if not title :
332
- create_select = False
371
+ else :
372
+ title = None
373
+
374
+ if not title :
375
+ create_select = False
333
376
334
377
if embed .description :
335
378
description = embed .description [:40 ].replace ("*" , "" ).replace ("`" , "" ).strip ()
@@ -352,7 +395,7 @@ def add_page(self, item: Embed) -> None:
352
395
async def _create_base (self , item : Embed , view : View ) -> None :
353
396
self .base = await self .destination .send (embed = item , view = view )
354
397
355
- def _show_page (self , page ):
398
+ def _show_page (self , page ) -> typing . Dict :
356
399
return dict (embed = page )
357
400
358
401
@@ -368,7 +411,7 @@ def add_page(self, item: str) -> None:
368
411
else :
369
412
raise TypeError ("Page must be a str object." )
370
413
371
- def _set_footer (self ):
414
+ def _set_footer (self ) -> None :
372
415
if self .embed is not None :
373
416
footer_text = f"Page { self .current + 1 } of { len (self .pages )} "
374
417
if self .footer_text :
0 commit comments