base.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. """Base classes for radix-themes components."""
  2. from __future__ import annotations
  3. from typing import Any, Dict, Literal, Optional, Union
  4. from reflex.components import Component
  5. from reflex.components.tags import Tag
  6. from reflex.utils import imports
  7. from reflex.vars import Var
  8. LiteralAlign = Literal["start", "center", "end", "baseline", "stretch"]
  9. LiteralJustify = Literal["start", "center", "end", "between"]
  10. LiteralSpacing = Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
  11. LiteralVariant = Literal["classic", "solid", "soft", "surface", "outline", "ghost"]
  12. LiteralAppearance = Literal["inherit", "light", "dark"]
  13. LiteralGrayColor = Literal["gray", "mauve", "slate", "sage", "olive", "sand", "auto"]
  14. LiteralPanelBackground = Literal["solid", "translucent"]
  15. LiteralRadius = Literal["none", "small", "medium", "large", "full"]
  16. LiteralScaling = Literal["90%", "95%", "100%", "105%", "110%"]
  17. LiteralAccentColor = Literal[
  18. "tomato",
  19. "red",
  20. "ruby",
  21. "crimson",
  22. "pink",
  23. "plum",
  24. "purple",
  25. "violet",
  26. "iris",
  27. "indigo",
  28. "blue",
  29. "cyan",
  30. "teal",
  31. "jade",
  32. "green",
  33. "grass",
  34. "brown",
  35. "orange",
  36. "sky",
  37. "mint",
  38. "lime",
  39. "yellow",
  40. "amber",
  41. "gold",
  42. "bronze",
  43. "gray",
  44. ]
  45. class CommonMarginProps(Component):
  46. """Many radix-themes elements accept shorthand margin props."""
  47. # Margin: "0" - "9"
  48. m: Optional[Var[LiteralSpacing]] = None
  49. # Margin horizontal: "0" - "9"
  50. mx: Optional[Var[LiteralSpacing]] = None
  51. # Margin vertical: "0" - "9"
  52. my: Optional[Var[LiteralSpacing]] = None
  53. # Margin top: "0" - "9"
  54. mt: Optional[Var[LiteralSpacing]] = None
  55. # Margin right: "0" - "9"
  56. mr: Optional[Var[LiteralSpacing]] = None
  57. # Margin bottom: "0" - "9"
  58. mb: Optional[Var[LiteralSpacing]] = None
  59. # Margin left: "0" - "9"
  60. ml: Optional[Var[LiteralSpacing]] = None
  61. class RadixThemesComponent(Component):
  62. """Base class for all @radix-ui/themes components."""
  63. library: str = "@radix-ui/themes@^2.0.0"
  64. # "Fake" prop color_scheme is used to avoid shadowing CSS prop "color".
  65. _rename_props: Dict[str, str] = {"colorScheme": "color"}
  66. @classmethod
  67. def create(
  68. cls,
  69. *children,
  70. **props,
  71. ) -> Component:
  72. """Create a new component instance.
  73. Will prepend "RadixThemes" to the component tag to avoid conflicts with
  74. other UI libraries for common names, like Text and Button.
  75. Args:
  76. *children: Child components.
  77. **props: Component properties.
  78. Returns:
  79. A new component instance.
  80. """
  81. component = super().create(*children, **props)
  82. if component.library is None:
  83. component.library = RadixThemesComponent.model_fields["library"].default
  84. component.alias = "RadixThemes" + (
  85. component.tag or component.__class__.__name__
  86. )
  87. return component
  88. @staticmethod
  89. def _get_app_wrap_components() -> dict[tuple[int, str], Component]:
  90. return {
  91. (45, "RadixThemesColorModeProvider"): RadixThemesColorModeProvider.create(),
  92. }
  93. class Theme(RadixThemesComponent):
  94. """A theme provider for radix components.
  95. This should be applied as `App.theme` to apply the theme to all radix
  96. components in the app with the given settings.
  97. It can also be used in a normal page to apply specified properties to all
  98. child elements as an override of the main theme.
  99. """
  100. tag: str = "Theme"
  101. # Whether to apply the themes background color to the theme node. Defaults to True.
  102. has_background: Optional[Var[bool]] = None
  103. # Override light or dark mode theme: "inherit" | "light" | "dark". Defaults to "inherit".
  104. appearance: Optional[Var[LiteralAppearance]] = None
  105. # The color used for default buttons, typography, backgrounds, etc
  106. accent_color: Optional[Var[LiteralAccentColor]] = None
  107. # The shade of gray, defaults to "auto".
  108. gray_color: Optional[Var[LiteralGrayColor]] = None
  109. # Whether panel backgrounds are translucent: "solid" | "translucent" (default)
  110. panel_background: Optional[Var[LiteralPanelBackground]] = None
  111. # Element border radius: "none" | "small" | "medium" | "large" | "full". Defaults to "medium".
  112. radius: Optional[Var[LiteralRadius]] = None
  113. # Scale of all theme items: "90%" | "95%" | "100%" | "105%" | "110%". Defaults to "100%"
  114. scaling: Optional[Var[LiteralScaling]] = None
  115. @classmethod
  116. def create(
  117. cls,
  118. *children,
  119. color_mode: LiteralAppearance | None = None,
  120. theme_panel: bool = False,
  121. accent_color: Union[LiteralAccentColor, Var[LiteralAccentColor]] | None = None,
  122. **props,
  123. ) -> Component:
  124. """Create a new Radix Theme specification.
  125. Args:
  126. *children: Child components.
  127. color_mode: Map to appearance prop.
  128. theme_panel: Whether to include a panel for editing the theme.
  129. **props: Component properties.
  130. Returns:
  131. A new component instance.
  132. """
  133. if color_mode is not None:
  134. props["appearance"] = color_mode
  135. if theme_panel:
  136. children = [ThemePanel.create(), *children]
  137. if not isinstance(accent_color, Var):
  138. accent_color = Var.create(accent_color)
  139. props["accent_color"] = accent_color
  140. return super().create(*children, **props)
  141. def _get_imports(self) -> imports.ImportDict:
  142. return imports.merge_imports(
  143. super()._get_imports(),
  144. {
  145. "": [
  146. imports.ImportVar(tag="@radix-ui/themes/styles.css", install=False)
  147. ],
  148. "/utils/theme.js": [
  149. imports.ImportVar(tag="theme", is_default=True),
  150. ],
  151. },
  152. )
  153. def _render(self, props: dict[str, Any] | None = None) -> Tag:
  154. tag = super()._render(props)
  155. tag.add_props(
  156. css=Var.create(
  157. "{{...theme.styles.global[':root'], ...theme.styles.global.body}}",
  158. _var_is_local=False,
  159. ),
  160. )
  161. return tag
  162. class ThemePanel(RadixThemesComponent):
  163. """Visual editor for creating and editing themes.
  164. Include as a child component of Theme to use in your app.
  165. """
  166. tag: str = "ThemePanel"
  167. # Whether the panel is open. Defaults to False.
  168. default_open: Optional[Var[bool]] = None
  169. class RadixThemesColorModeProvider(Component):
  170. """Next-themes integration for radix themes components."""
  171. library: str = "/components/reflex/radix_themes_color_mode_provider.js"
  172. tag: str = "RadixThemesColorModeProvider"
  173. is_default: bool = True
  174. theme = Theme.create
  175. theme_panel = ThemePanel.create