From f1a8c2e31dc6f9e216d67fe096c5ddb324897c50 Mon Sep 17 00:00:00 2001 From: Paillat Date: Sat, 3 Aug 2024 15:27:07 +0200 Subject: [PATCH] Finally fixed --- examples/color_select.py | 95 +++++++++++++++++++++++++ pyproject.toml | 2 +- src/pycord_reactive_views/__init__.py | 4 +- src/pycord_reactive_views/components.py | 33 +++++++++ src/pycord_reactive_views/view.py | 2 + 5 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 examples/color_select.py diff --git a/examples/color_select.py b/examples/color_select.py new file mode 100644 index 0000000..c4f2ce9 --- /dev/null +++ b/examples/color_select.py @@ -0,0 +1,95 @@ +# ruff: noqa: INP001 +import os + +import discord +from dotenv import load_dotenv +from pycord_reactive_views import ReactiveSelect, ReactiveValue, ReactiveView + +load_dotenv() + +bot = discord.Bot() + +colors: dict[str, list[discord.Colour]] = { + "red": [discord.Colour.red(), discord.Colour.dark_red(), discord.Colour.brand_red()], + "green": [discord.Colour.green(), discord.Colour.dark_green(), discord.Colour.brand_green()], + "blue": [discord.Colour.blue(), discord.Colour.dark_blue()], + "yellow": [discord.Colour.gold(), discord.Colour.dark_gold()], + "purple": [ + discord.Colour.purple(), + discord.Colour.dark_purple(), + discord.Colour.blurple(), + discord.Colour.og_blurple(), + ], + "gray": [discord.Colour.greyple(), discord.Colour.dark_grey(), discord.Colour.light_grey()], + "teal": [discord.Colour.teal(), discord.Colour.dark_teal()], + "magenta": [discord.Colour.magenta(), discord.Colour.dark_magenta()], + "orange": [discord.Colour.orange(), discord.Colour.dark_orange()], +} + + +class ColourSelector(ReactiveView): + """A simple view that allows you to select a colour and shade and updates the embed based on the selection.""" + + def __init__(self): + super().__init__() + self.colour: str = "red" + self.shade: discord.Colour = discord.Colour.red() + self.colour_select = discord.ui.Select( + options=[ + discord.SelectOption( + label=colour.capitalize(), + value=colour, + ) + for colour in colors + ], + placeholder="Select a colour", + custom_id="colour_select", + ) + self.shade_select = ReactiveSelect( + custom_id="shade_select", + placeholder="Select a shade", + options=ReactiveValue( + lambda: [ + discord.SelectOption( + label=str(shade.value), + ) + for shade in colors[self.colour] + ], + [ + discord.SelectOption( + label=str(shade.value), + ) + for shade in colors[self.colour] + ], + ), + ) + self.colour_select.callback = self._colour_select_callback + self.shade_select.callback = self._shade_select_callback + self.add_item(self.colour_select) + self.add_item(self.shade_select) + + async def _colour_select_callback(self, interaction: discord.Interaction) -> None: + await interaction.response.defer() + self.colour = self.colour_select.values[0] # pyright: ignore[reportAttributeAccessIssue] + await self.update() + + async def _shade_select_callback(self, interaction: discord.Interaction) -> None: + await interaction.response.defer() + selected_shade = int(self.shade_select.values[0]) # pyright: ignore[reportArgumentType] + self.shade = discord.Colour(selected_shade) + await self.update() + + async def _get_embed(self) -> discord.Embed | None: + return discord.Embed( + title=f"{self.colour.capitalize()} {self.shade}", + colour=self.shade, + ) + + +@bot.slash_command() +async def colours(ctx: discord.ApplicationContext) -> None: + """Send the colour selector view.""" + await ColourSelector().send(ctx) + + +bot.run(os.getenv("TOKEN")) diff --git a/pyproject.toml b/pyproject.toml index 8e244b4..54735f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12" ] -requires-python = "<3.12.0,>=3.11" +requires-python = ">=3.11" dependencies = [ "py-cord>=2.6.0", ] diff --git a/src/pycord_reactive_views/__init__.py b/src/pycord_reactive_views/__init__.py index a532c4d..942b1fb 100644 --- a/src/pycord_reactive_views/__init__.py +++ b/src/pycord_reactive_views/__init__.py @@ -1,5 +1,5 @@ -from .components import ReactiveButton +from .components import ReactiveButton, ReactiveSelect from .utils import ReactiveValue from .view import ReactiveView -__all__ = ["ReactiveButton", "ReactiveView", "ReactiveValue"] +__all__ = ["ReactiveButton", "ReactiveSelect", "ReactiveValue", "ReactiveView"] diff --git a/src/pycord_reactive_views/components.py b/src/pycord_reactive_views/components.py index cdb9d30..2a0200e 100644 --- a/src/pycord_reactive_views/components.py +++ b/src/pycord_reactive_views/components.py @@ -51,3 +51,36 @@ class ReactiveButton(discord.ui.Button, Reactive): # pyright: ignore[reportUnsa self.add_reactive("url", url) self.add_reactive("emoji", emoji) self.add_reactive("row", row) + self.custom_id = custom_id + self.sku_id = sku_id + + +class ReactiveSelect(discord.ui.Select, Reactive): # pyright: ignore[reportUnsafeMultipleInheritance,reportMissingTypeArgument] + """A select menu that can be used with reactive values.""" + + def __init__( + self, + select_type: discord.ComponentType = discord.ComponentType.string_select, + *, + custom_id: str | None = None, + placeholder: MaybeReactiveValue[str | None] = None, + min_values: MaybeReactiveValue[int] = 1, + max_values: MaybeReactiveValue[int] = 1, + options: MaybeReactiveValue[list[discord.SelectOption] | None] = None, + channel_types: MaybeReactiveValue[list[discord.ChannelType] | None] = None, + disabled: MaybeReactiveValue[bool] = False, + row: MaybeReactiveValue[int | None] = None, + ): + discord.ui.Select.__init__(self) + Reactive.__init__(self) + self.add_reactive("placeholder", placeholder) + self.add_reactive("min_values", min_values) + self.add_reactive("max_values", max_values) + self.add_reactive("options", options) + if select_type == discord.ComponentType.channel_select: + self.add_reactive("channel_types", channel_types) + self.add_reactive("disabled", disabled) + self.add_reactive("row", row) + if custom_id: + self.custom_id = custom_id + self.select_type = select_type diff --git a/src/pycord_reactive_views/view.py b/src/pycord_reactive_views/view.py index 7a3c1bb..849b040 100644 --- a/src/pycord_reactive_views/view.py +++ b/src/pycord_reactive_views/view.py @@ -28,6 +28,8 @@ class ReactiveView(discord.ui.View): async def _get_embeds(self) -> list[discord.Embed]: """Get the discord embeds to be displayed in the message.""" + if embed := await self._get_embed(): + return [embed] return [] async def _get_content(self) -> str | None: