Skip to content

Commit 620b10f

Browse files
committed
add various ui component
1 parent 0724882 commit 620b10f

File tree

9 files changed

+496
-0
lines changed

9 files changed

+496
-0
lines changed
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .custom_dropdown import CustomDropdown
2+
3+
__all__ = ["CustomDropdown"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import os
2+
from typing import Callable
3+
4+
import discord
5+
from discord import ui
6+
from typing_extensions import override
7+
8+
__all__ = ["CustomDropdown"]
9+
10+
11+
DropdownCallback = Callable[[discord.Interaction], str | None]
12+
"""
13+
A callback for a dropdown.
14+
15+
Parameters:
16+
-----------
17+
interaction: discord.Interaction
18+
The interaction object.
19+
20+
Returns:
21+
--------
22+
``str`` | ``None``
23+
Return with a string to mark it as an error message, otherwise None.
24+
"""
25+
26+
27+
class CustomDropdown(ui.Select[ui.View]):
28+
def __init__(
29+
self,
30+
custom_id: str | None = None,
31+
placeholder: str | None = "Select an option",
32+
min_values: int = 1,
33+
max_values: int = 1,
34+
disabled: bool = False,
35+
):
36+
self._callback: list[DropdownCallback] = []
37+
if custom_id is None:
38+
custom_id = "nameless-dropdown-" + os.urandom(16).hex()
39+
40+
super().__init__(
41+
custom_id=custom_id,
42+
placeholder=placeholder,
43+
min_values=min_values,
44+
max_values=max_values,
45+
disabled=disabled,
46+
options=[],
47+
)
48+
49+
def add_callback(self, callback: DropdownCallback):
50+
self._callback.append(callback)
51+
return self
52+
53+
@override
54+
async def callback(self, interaction: discord.Interaction):
55+
await interaction.response.defer()
56+
for callback in self._callback:
57+
error = callback(interaction)
58+
if error:
59+
await interaction.response.send_message(error, ephemeral=True)
60+
return
61+
62+
if self.view is not None:
63+
self.view.stop()
64+
65+
def self_add_option(
66+
self,
67+
*,
68+
label: str,
69+
value: str = "",
70+
description: str | None = None,
71+
emoji: str | discord.Emoji | discord.PartialEmoji | None = None,
72+
default: bool = False,
73+
):
74+
self.add_option(
75+
label=label,
76+
value=value,
77+
description=description,
78+
emoji=emoji,
79+
default=default,
80+
)
81+
return self

nameless/custom/ui/modal/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .custom_input import CustomInput
2+
from .custom_modal import BaseCustomModal
3+
4+
__all__ = ["CustomInput", "BaseCustomModal"]
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from typing import Generic, TypeVar, override
2+
3+
import discord
4+
from discord import ui
5+
6+
V = TypeVar("V", bound=type, covariant=True)
7+
8+
9+
class CustomInput(ui.TextInput[ui.Modal], Generic[V]):
10+
def __init__(
11+
self, label: str, custom_id: str, default: str = "0", convert: V = str
12+
) -> None:
13+
super().__init__(
14+
label=label, custom_id=custom_id, placeholder=default, default=default
15+
)
16+
self.convert: V = convert
17+
self.input: V = convert(default)
18+
19+
@override
20+
async def callback(self, interaction: discord.Interaction):
21+
self.input = self.convert(self.value)
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from typing import Generic, TypeVar, override
2+
3+
import discord
4+
5+
from .custom_input import CustomInput
6+
7+
V = TypeVar("V", bound=type, covariant=True)
8+
9+
10+
class BaseCustomModal(discord.ui.Modal, Generic[V]):
11+
def __init__(self, title: str) -> None:
12+
super().__init__(timeout=30, title=title)
13+
14+
@override
15+
async def on_submit(self, interaction: discord.Interaction[discord.Client]) -> None:
16+
await interaction.response.defer()
17+
self.stop()
18+
19+
def get_input(self) -> CustomInput[V]:
20+
return self.children[0] # pyright: ignore[reportReturnType]
21+
22+
@property
23+
def value(self) -> V:
24+
return self.get_input().input

nameless/custom/ui/view/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .view_button import ViewButton
2+
from .view_menu import ViewMenu
3+
4+
__all__ = ["ViewButton", "ViewMenu"]

nameless/custom/ui/view/base.py

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
from abc import ABC, abstractmethod
2+
from collections.abc import Iterable
3+
4+
import discord
5+
from discord.ui.button import Button
6+
from discord.ui.view import View
7+
8+
9+
class BaseView(View, ABC):
10+
@abstractmethod
11+
def add_pages(self, pages: Iterable[discord.Embed]):
12+
"""
13+
Add multiple pages to the view.
14+
15+
Parameters:
16+
-----------
17+
pages: Iterable[discord.Embed]
18+
An iterable of discord.Embed objects to add to the view.
19+
"""
20+
pass
21+
22+
@abstractmethod
23+
def add_button(self, button: Button["BaseView"]):
24+
"""
25+
Add a button to the view.
26+
27+
Parameters:
28+
-----------
29+
button: BaseButton
30+
The button to add to the view.
31+
"""
32+
pass
33+
34+
@abstractmethod
35+
async def next_page(self):
36+
"""
37+
Go to the next page.
38+
"""
39+
pass
40+
41+
@abstractmethod
42+
async def previous_page(self):
43+
"""
44+
Go to the previous page.
45+
"""
46+
pass
47+
48+
@abstractmethod
49+
async def go_to_first_page(self):
50+
"""
51+
Go to the first page.
52+
"""
53+
pass
54+
55+
@abstractmethod
56+
async def go_to_last_page(self):
57+
"""
58+
Go to the last page.
59+
"""
60+
pass
61+
62+
@abstractmethod
63+
async def go_to_page(self, page: int):
64+
"""
65+
Go to a specific page.
66+
67+
Parameters:
68+
-----------
69+
page: int
70+
The page number to go to.
71+
"""
72+
pass
73+
74+
@abstractmethod
75+
async def end(self):
76+
"""
77+
End the view.
78+
"""
79+
pass
80+
81+
@abstractmethod
82+
async def start(self) -> bool:
83+
"""
84+
Start the view. This method should be called
85+
after adding all the pages and buttons.
86+
87+
This method will be blocking and wait for the view to finish interacting.
88+
89+
Returns:
90+
--------
91+
bool:
92+
Return ``True`` if the view timed out, otherwise ``False``.
93+
"""
94+
pass

0 commit comments

Comments
 (0)