discordcn 0.0.1a3__tar.gz → 0.0.1a4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: discordcn
3
- Version: 0.0.1a3
3
+ Version: 0.0.1a4
4
4
  Summary: An unofficial, community-driven implementation for Discord app interface patterns.
5
5
  Project-URL: Homepage, https://github.com/nicebots-xyz/discordcn-py
6
- Project-URL: source_archive, https://github.com/nicebots-xyz/discordcn-py/archive/3d2717cc3b78e67dfbbef52df1bc92f0406e734d.zip
6
+ Project-URL: source_archive, https://github.com/nicebots-xyz/discordcn-py/archive/b4a168d863738ba6dfaa0f6dfa823336eebd447f.zip
7
7
  Author-email: Paillat-dev <me@paillat.dev>
8
8
  License-Expression: MIT
9
9
  License-File: LICENSE
@@ -77,7 +77,8 @@ extend-ignore = [
77
77
  "ISC003", # explicit-string-concatenation conflicts with basedpyright reportImplicitStringConcatenation
78
78
  "TRY003", # raise-vanilla-args
79
79
  "COM812", # missing-trailing-comma conflicts with formatter
80
- "ANN401"# any-type we allow Any
80
+ "ANN401", # any-type we allow Any
81
+ "PLR0913" # too-many-arguments 5+ args is fine
81
82
  ]
82
83
  pydocstyle.convention = "google"
83
84
 
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.0.1a3'
32
- __version_tuple__ = version_tuple = (0, 0, 1, 'a3')
31
+ __version__ = version = '0.0.1a4'
32
+ __version_tuple__ = version_tuple = (0, 0, 1, 'a4')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -12,6 +12,7 @@ from .interfaces import (
12
12
  PageIndicatorButton,
13
13
  PaginatorControls,
14
14
  PaginatorControlsBase,
15
+ PaginatorIndicatorModal,
15
16
  PaginatorInterface,
16
17
  PaginatorInterfaceBase,
17
18
  )
@@ -23,6 +24,7 @@ __all__ = (
23
24
  "PageIndicatorButton",
24
25
  "PaginatorControls",
25
26
  "PaginatorControlsBase",
27
+ "PaginatorIndicatorModal",
26
28
  "PaginatorInterface",
27
29
  "PaginatorInterfaceBase",
28
30
  )
@@ -7,6 +7,7 @@ from .paginator import (
7
7
  PageIndicatorButton,
8
8
  PaginatorControls,
9
9
  PaginatorControlsBase,
10
+ PaginatorIndicatorModal,
10
11
  PaginatorInterface,
11
12
  PaginatorInterfaceBase,
12
13
  )
@@ -18,6 +19,7 @@ __all__ = (
18
19
  "PageIndicatorButton",
19
20
  "PaginatorControls",
20
21
  "PaginatorControlsBase",
22
+ "PaginatorIndicatorModal",
21
23
  "PaginatorInterface",
22
24
  "PaginatorInterfaceBase",
23
25
  )
@@ -11,6 +11,7 @@ from collections.abc import Sequence
11
11
  from typing import Any, Generic, TypeVar
12
12
 
13
13
  import discord
14
+ from discord import Interaction
14
15
  from typing_extensions import Self, override
15
16
 
16
17
  from discordcn.pycord._utils import maybe_awaitable
@@ -23,12 +24,12 @@ T = TypeVar("T", bound="PaginatorInterfaceBase")
23
24
  class PaginatorInterfaceBase(ABC, discord.ui.DesignerView):
24
25
  """ABC defining the interface for paginator views.
25
26
 
26
- This ABC specifies the minimum requirements for a view to be
27
- compatible with paginator controls, ensuring consistent behavior across
28
- different paginator implementations.
27
+ This ABC specifies the minimum requirements for a view to be compatible
28
+ with paginator controls, ensuring consistent behavior across different
29
+ paginator implementations.
29
30
 
30
31
  Attributes:
31
- page: The current page index (0-based).
32
+ page (int): The current page index (0-based).
32
33
  """
33
34
 
34
35
  page: int
@@ -65,12 +66,10 @@ class PaginatorControlsBase(ABC, discord.ui.ActionRow[T], Generic[T]):
65
66
  class PageButton(discord.ui.Button[PaginatorInterfaceBase]):
66
67
  """Navigation button for jumping to a specific page.
67
68
 
68
- This button updates the parent view's page index and refreshes the UI
69
- when clicked. It is designed to work with any view implementing
70
- PaginatorInterfaceBase.
69
+ Updates the parent view's page index and refreshes the UI when clicked.
71
70
 
72
71
  Attributes:
73
- page: The target page index this button navigates to.
72
+ page (int): The target page index this button navigates to.
74
73
  """
75
74
 
76
75
  def __init__(self, page: int, **kwargs: Any) -> None:
@@ -78,9 +77,8 @@ class PageButton(discord.ui.Button[PaginatorInterfaceBase]):
78
77
 
79
78
  Args:
80
79
  page: The target page index (0-based) to navigate to when clicked.
81
- **kwargs: Additional keyword arguments passed to the parent
82
- discord.ui.Button constructor (e.g., ``emoji``, ``style``,
83
- ``disabled``).
80
+ **kwargs: Additional keyword arguments passed to discord.ui.Button
81
+ (e.g., ``emoji``, ``style``, ``disabled``).
84
82
  """
85
83
  super().__init__(**kwargs)
86
84
  self.page: int = page
@@ -94,6 +92,9 @@ class PageButton(discord.ui.Button[PaginatorInterfaceBase]):
94
92
 
95
93
  Args:
96
94
  interaction: The interaction object from the button click.
95
+
96
+ Raises:
97
+ TypeError: If the button is not attached to a view.
97
98
  """
98
99
  if self.view is None:
99
100
  msg = "This button is not attached to a view."
@@ -107,54 +108,143 @@ class PageButton(discord.ui.Button[PaginatorInterfaceBase]):
107
108
  await interaction.response.edit_message(view=v)
108
109
 
109
110
 
111
+ class PaginatorIndicatorModal(discord.ui.DesignerModal):
112
+ """Modal for direct page navigation input.
113
+
114
+ Allows users to jump to a specific page by entering a page number.
115
+
116
+ Attributes:
117
+ view (PaginatorInterfaceBase): The parent paginator view.
118
+ input (discord.ui.TextInput): The text input field for page number.
119
+ invalid (str): Error message shown for invalid page numbers.
120
+ """
121
+
122
+ def __init__(
123
+ self, modal_title: str, label_title: str, label_description: str, invalid: str, view: PaginatorInterfaceBase
124
+ ) -> None:
125
+ """Initialize the page navigation modal.
126
+
127
+ Args:
128
+ modal_title: The title displayed at the top of the modal.
129
+ label_title: The label text for the input field.
130
+ label_description: The description text for the input field.
131
+ invalid: Error message shown when an invalid page number is entered.
132
+ view: The parent paginator view.
133
+ """
134
+ self.view: PaginatorInterfaceBase = view
135
+ self.input: discord.ui.TextInput = discord.ui.TextInput(
136
+ style=discord.InputTextStyle.short, max_length=len(str(view.max_page))
137
+ )
138
+ self.invalid: str = invalid
139
+ label: discord.ui.Label[Self] = discord.ui.Label(
140
+ label=label_title, description=label_description, item=self.input
141
+ )
142
+ super().__init__(label, title=modal_title)
143
+
144
+ @override
145
+ async def callback(self, interaction: Interaction) -> None:
146
+ """Handle modal submission.
147
+
148
+ Validates the page number and navigates to the specified page if valid.
149
+
150
+ Args:
151
+ interaction: The interaction object from the modal submission.
152
+ """
153
+ await interaction.response.defer()
154
+ page = self.input.value
155
+ if not page or not page.isdigit() or (int_page := int(page) - 1) > self.view.max_page:
156
+ await interaction.respond(self.invalid, ephemeral=True)
157
+ return
158
+ self.view.page = int_page
159
+ await maybe_awaitable(self.view.update())
160
+ await interaction.edit(view=self.view)
161
+
162
+
110
163
  class PageIndicatorButton(discord.ui.Button[PaginatorInterfaceBase]):
111
- """Non-interactive button displaying the current page number.
164
+ """Button displaying the current page number.
165
+
166
+ Shows the current page position in the format "X/Y". Can optionally be
167
+ made interactable to open a modal for direct page navigation.
112
168
 
113
- This button serves as a visual indicator of the current page position
114
- and is always disabled to prevent interaction. It displays text in the
115
- format "Page X/Y".
169
+ Attributes:
170
+ disabled (bool): Whether the button is disabled.
171
+ modal_title (str): Title for the page navigation modal.
172
+ label_title (str): Label title for the modal input field.
173
+ label_description (str): Description for the modal input field.
174
+ invalid (str): Error message for invalid page input.
116
175
  """
117
176
 
118
- def __init__(self, *, label: str) -> None:
177
+ def __init__(
178
+ self,
179
+ *,
180
+ label: str,
181
+ interactable: bool,
182
+ modal_title: str,
183
+ label_title: str,
184
+ label_description: str,
185
+ invalid: str,
186
+ ) -> None:
119
187
  """Initialize a page indicator button.
120
188
 
121
189
  Args:
122
- label: The text to display on the button (typically "Page X/Y").
190
+ label: The text to display on the button (typically "X/Y" format).
191
+ interactable: Whether clicking the button should open a navigation modal.
192
+ modal_title: Title for the page navigation modal.
193
+ label_title: Label for the modal's input field.
194
+ label_description: Description for the modal's input field.
195
+ invalid: Error message shown for invalid page numbers.
123
196
  """
124
197
  super().__init__(style=discord.ButtonStyle.secondary, label=label)
125
- self.disabled: bool = True
198
+ self.disabled: bool = not interactable
199
+ self.modal_title: str = modal_title
200
+ self.label_title: str = label_title
201
+ self.label_description: str = label_description
202
+ self.invalid: str = invalid
126
203
 
127
204
  @override
128
205
  async def callback(self, interaction: discord.Interaction) -> None:
129
- """Handle button click interactions (no-op).
206
+ """Handle button click interactions.
130
207
 
131
- This callback defers the interaction invisibly since the button is
132
- disabled and should never be clicked.
208
+ Opens a modal for direct page navigation when clicked.
133
209
 
134
210
  Args:
135
211
  interaction: The interaction object from the button click.
212
+
213
+ Raises:
214
+ TypeError: If the button is not attached to a view.
136
215
  """
137
- await interaction.response.defer(invisible=True)
216
+ if self.view is None:
217
+ msg = "This button is not attached to a view."
218
+ raise TypeError(msg)
219
+
220
+ await interaction.response.send_modal(
221
+ PaginatorIndicatorModal( # pyright: ignore[reportArgumentType]
222
+ modal_title=self.modal_title,
223
+ label_title=self.label_title,
224
+ label_description=self.label_description,
225
+ invalid=self.invalid,
226
+ view=self.view,
227
+ )
228
+ )
138
229
 
139
230
 
140
231
  class PaginatorControls(PaginatorControlsBase[T], Generic[T]):
141
232
  """Navigation controls row for paginator interfaces.
142
233
 
143
- This action row contains five elements:
144
- - First page button (⏮️): Jump to page 0
145
- - Previous page button (◀️): Go back one page
146
- - Page indicator: Display current page position (non-interactive)
147
- - Next page button (▶️): Advance one page
148
- - Last page button (⏭️): Jump to the final page
149
-
150
- Buttons are automatically disabled when they would navigate beyond valid
151
- page boundaries (e.g., previous/first buttons on page 0).
152
-
153
- Attributes:
154
- FIRST_EMOJI: The emoji to use for the "first page" button.
155
- LEFT_EMOJI: The emoji to use for the "previous page" button.
156
- RIGHT_EMOJI: The emoji to use for the "next page" button.
157
- LAST_EMOJI: The emoji to use for the "last page" button.
234
+ Provides five navigation elements: first page, previous page, page indicator,
235
+ next page, and last page. Buttons are automatically disabled when they would
236
+ navigate beyond valid page boundaries.
237
+
238
+ Class Attributes:
239
+ FIRST_EMOJI (EmojiType): Emoji for the "first page" button.
240
+ LEFT_EMOJI (EmojiType): Emoji for the "previous page" button.
241
+ RIGHT_EMOJI (EmojiType): Emoji for the "next page" button.
242
+ LAST_EMOJI (EmojiType): Emoji for the "last page" button.
243
+ INTERACTABLE_INDICATOR (bool): Whether the page indicator opens a navigation modal.
244
+ MODAL_TITLE (str): Title for the page navigation modal.
245
+ MODAL_LABEL_TITLE (str): Label for the modal's input field.
246
+ MODAL_LABEL_DESCRIPTION (str): Description for the modal's input field.
247
+ MODAL_INVALID (str): Error message for invalid page input.
158
248
  """
159
249
 
160
250
  FIRST_EMOJI: EmojiType = discord.PartialEmoji(name="⏮️")
@@ -162,6 +252,12 @@ class PaginatorControls(PaginatorControlsBase[T], Generic[T]):
162
252
  RIGHT_EMOJI: EmojiType = discord.PartialEmoji(name="▶️")
163
253
  LAST_EMOJI: EmojiType = discord.PartialEmoji(name="⏭️")
164
254
 
255
+ INTERACTABLE_INDICATOR: bool = True
256
+ MODAL_TITLE: str = "Page Navigation"
257
+ MODAL_LABEL_TITLE: str = "Page Number:"
258
+ MODAL_LABEL_DESCRIPTION: str = "Input a page number to jump to that page."
259
+ MODAL_INVALID: str = "Please input a valid page number."
260
+
165
261
  def __init__(
166
262
  self,
167
263
  view: T,
@@ -194,6 +290,11 @@ class PaginatorControls(PaginatorControlsBase[T], Generic[T]):
194
290
  self.add_item(
195
291
  PageIndicatorButton(
196
292
  label=f"{view.page + 1}/{view.max_page + 1}",
293
+ interactable=self.INTERACTABLE_INDICATOR,
294
+ modal_title=self.MODAL_TITLE,
295
+ label_title=self.MODAL_LABEL_TITLE,
296
+ label_description=self.MODAL_LABEL_DESCRIPTION,
297
+ invalid=self.MODAL_INVALID,
197
298
  ),
198
299
  )
199
300
  self.add_item(
@@ -217,19 +318,16 @@ class PaginatorControls(PaginatorControlsBase[T], Generic[T]):
217
318
  class PaginatorInterface(PaginatorInterfaceBase):
218
319
  """Multi-page view with automatic navigation controls.
219
320
 
220
- This view manages navigation between multiple pages of UI components,
221
- automatically displaying the appropriate page content and navigation
222
- controls. Each page is defined as a sequence of view items (buttons,
223
- selects, etc.) that are dynamically loaded when navigating between pages.
224
-
225
- The paginator automatically appends navigation controls to the bottom of
226
- each page, allowing users to move between pages using first/previous/next/last
227
- buttons and see their current position.
321
+ Manages navigation between multiple pages of UI components, automatically
322
+ displaying the appropriate page content and navigation controls. Each page
323
+ is dynamically loaded when navigating between pages.
228
324
 
229
325
  Attributes:
230
- pages: The sequence of pages, where each page is a sequence of view items.
231
- page: The current page index (0-based).
232
- controls: The paginator controls class to use for navigation.
326
+ pages (Sequence[Sequence[discord.ui.ViewItem[discord.ui.DesignerView]]]):
327
+ The sequence of pages, where each page is a sequence of view items.
328
+ page (int): The current page index (0-based).
329
+ controls (type[PaginatorControlsBase[Self]]): The paginator controls
330
+ class to use for navigation.
233
331
  """
234
332
 
235
333
  def __init__(
@@ -258,7 +356,7 @@ class PaginatorInterface(PaginatorInterfaceBase):
258
356
  """The maximum page index (0-based).
259
357
 
260
358
  Returns:
261
- The index of the last page, equal to ``len(pages) - 1``.
359
+ The index of the last page.
262
360
  """
263
361
  return len(self.pages) - 1
264
362
 
@@ -273,7 +371,8 @@ class PaginatorInterface(PaginatorInterfaceBase):
273
371
  self.clear_items()
274
372
  for item in self.pages[self.page]:
275
373
  self.add_item(item)
276
- self.add_item(self.controls(self))
374
+ if len(self.pages) > 1:
375
+ self.add_item(self.controls(self))
277
376
 
278
377
 
279
378
  __all__ = (
@@ -281,6 +380,7 @@ __all__ = (
281
380
  "PageIndicatorButton",
282
381
  "PaginatorControls",
283
382
  "PaginatorControlsBase",
383
+ "PaginatorIndicatorModal",
284
384
  "PaginatorInterface",
285
385
  "PaginatorInterfaceBase",
286
386
  )
File without changes
File without changes