reflex/reflex/components/radix/themes/components/select.py
2025-01-29 17:41:10 -08:00

255 lines
7.8 KiB
Python

"""Interactive components provided by @radix-ui/themes."""
from typing import List, Literal, Union
import reflex as rx
from reflex.components.component import Component, ComponentNamespace
from reflex.components.core.breakpoints import Responsive
from reflex.constants.compiler import MemoizationMode
from reflex.event import no_args_event_spec, passthrough_event_spec
from reflex.vars.base import Var
from ..base import LiteralAccentColor, LiteralRadius, RadixThemesComponent
class SelectRoot(RadixThemesComponent):
"""Displays a list of options for the user to pick from, triggered by a button."""
tag = "Select.Root"
# The size of the select: "1" | "2" | "3"
size: Var[Responsive[Literal["1", "2", "3"]]]
# The value of the select when initially rendered. Use when you do not need to control the state of the select.
default_value: Var[str]
# The controlled value of the select. Should be used in conjunction with on_change.
value: Var[str]
# The open state of the select when it is initially rendered. Use when you do not need to control its open state.
default_open: Var[bool]
# The controlled open state of the select. Must be used in conjunction with on_open_change.
open: Var[bool]
# The name of the select control when submitting the form.
name: Var[str]
# When True, prevents the user from interacting with select.
disabled: Var[bool]
# When True, indicates that the user must select a value before the owning form can be submitted.
required: Var[bool]
# Props to rename
_rename_props = {"onChange": "onValueChange"}
# Fired when the value of the select changes.
on_change: rx.EventHandler[passthrough_event_spec(str)]
# Fired when the select is opened or closed.
on_open_change: rx.EventHandler[passthrough_event_spec(bool)]
class SelectTrigger(RadixThemesComponent):
"""The button that toggles the select."""
tag = "Select.Trigger"
# Variant of the select trigger
variant: Var[Literal["classic", "surface", "soft", "ghost"]]
# The color of the select trigger
color_scheme: Var[LiteralAccentColor]
# The radius of the select trigger
radius: Var[LiteralRadius]
# The placeholder of the select trigger
placeholder: Var[str]
_valid_parents: List[str] = ["SelectRoot"]
_memoization_mode = MemoizationMode(recursive=False)
class SelectContent(RadixThemesComponent):
"""The component that pops out when the select is open."""
tag = "Select.Content"
# The variant of the select content
variant: Var[Literal["solid", "soft"]]
# The color of the select content
color_scheme: Var[LiteralAccentColor]
# Whether to render the select content with higher contrast color against background
high_contrast: Var[bool]
# The positioning mode to use, item-aligned is the default and behaves similarly to a native MacOS menu by positioning content relative to the active item. popper positions content in the same way as our other primitives, for example Popover or DropdownMenu.
position: Var[Literal["item-aligned", "popper"]]
# The preferred side of the anchor to render against when open. Will be reversed when collisions occur and avoidCollisions is enabled. Only available when position is set to popper.
side: Var[Literal["top", "right", "bottom", "left"]]
# The distance in pixels from the anchor. Only available when position is set to popper.
side_offset: Var[int]
# The preferred alignment against the anchor. May change when collisions occur. Only available when position is set to popper.
align: Var[Literal["start", "center", "end"]]
# The vertical distance in pixels from the anchor. Only available when position is set to popper.
align_offset: Var[int]
# Fired when the select content is closed.
on_close_auto_focus: rx.EventHandler[no_args_event_spec]
# Fired when the escape key is pressed.
on_escape_key_down: rx.EventHandler[no_args_event_spec]
# Fired when a pointer down event happens outside the select content.
on_pointer_down_outside: rx.EventHandler[no_args_event_spec]
class SelectGroup(RadixThemesComponent):
"""Used to group multiple items."""
tag = "Select.Group"
_valid_parents: List[str] = ["SelectContent"]
class SelectItem(RadixThemesComponent):
"""The component that contains the select items."""
tag = "Select.Item"
# The value given as data when submitting a form with a name.
value: Var[str]
# Whether the select item is disabled
disabled: Var[bool]
_valid_parents: List[str] = ["SelectGroup", "SelectContent"]
class SelectLabel(RadixThemesComponent):
"""Used to render the label of a group, it isn't focusable using arrow keys."""
tag = "Select.Label"
_valid_parents: List[str] = ["SelectGroup"]
class SelectSeparator(RadixThemesComponent):
"""Used to visually separate items in the Select."""
tag = "Select.Separator"
class HighLevelSelect(SelectRoot):
"""High level wrapper for the Select component."""
# The items of the select.
items: Var[List[str]]
# The placeholder of the select.
placeholder: Var[str]
# The label of the select.
label: Var[str]
# The color of the select.
color_scheme: Var[LiteralAccentColor]
# Whether to render the select with higher contrast color against background.
high_contrast: Var[bool]
# The variant of the select.
variant: Var[Literal["classic", "surface", "soft", "ghost"]]
# The radius of the select.
radius: Var[LiteralRadius]
# The width of the select.
width: Var[str]
# The positioning mode to use. Default is "item-aligned".
position: Var[Literal["item-aligned", "popper"]]
@classmethod
def create(cls, items: Union[List[str], Var[List[str]]], **props) -> Component:
"""Create a select component.
Args:
items: The items of the select.
**props: Additional properties to apply to the select component.
Returns:
The select component.
"""
trigger_prop_list = [
"placeholder",
"variant",
"radius",
"width",
"flex_shrink",
"custom_attrs",
]
content_props = {
prop: props.pop(prop)
for prop in ["high_contrast", "position"]
if prop in props
}
trigger_props = {
prop: props.pop(prop) for prop in trigger_prop_list if prop in props
}
color_scheme = props.pop("color_scheme", None)
if color_scheme is not None:
content_props["color_scheme"] = color_scheme
trigger_props["color_scheme"] = color_scheme
label = props.pop("label", None)
if isinstance(items, Var):
child = [
rx.foreach(items, lambda item: SelectItem.create(item, value=item))
]
else:
child = [SelectItem.create(item, value=item) for item in items]
return SelectRoot.create(
SelectTrigger.create(
**trigger_props,
),
SelectContent.create(
SelectGroup.create(
SelectLabel.create(label) if label is not None else "",
*child,
),
**content_props,
),
**props,
)
class Select(ComponentNamespace):
"""Select components namespace."""
root = staticmethod(SelectRoot.create)
trigger = staticmethod(SelectTrigger.create)
content = staticmethod(SelectContent.create)
group = staticmethod(SelectGroup.create)
item = staticmethod(SelectItem.create)
separator = staticmethod(SelectSeparator.create)
label = staticmethod(SelectLabel.create)
__call__ = staticmethod(HighLevelSelect.create)
select = Select()