base.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. """Base classes for radix-themes components."""
  2. from __future__ import annotations
  3. from typing import Any, Literal
  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. LiteralSize = Literal["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: Var[LiteralSize]
  49. # Margin horizontal: "0" - "9"
  50. mx: Var[LiteralSize]
  51. # Margin vertical: "0" - "9"
  52. my: Var[LiteralSize]
  53. # Margin top: "0" - "9"
  54. mt: Var[LiteralSize]
  55. # Margin right: "0" - "9"
  56. mr: Var[LiteralSize]
  57. # Margin bottom: "0" - "9"
  58. mb: Var[LiteralSize]
  59. # Margin left: "0" - "9"
  60. ml: Var[LiteralSize]
  61. class RadixThemesComponent(Component):
  62. """Base class for all @radix-ui/themes components."""
  63. library = "@radix-ui/themes@^2.0.0"
  64. @classmethod
  65. def create(
  66. cls,
  67. *children,
  68. color: Var[str] = None, # type: ignore
  69. color_scheme: Var[LiteralAccentColor] = None, # type: ignore
  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. color: map to CSS default color property.
  78. color_scheme: map to radix color property.
  79. **props: Component properties.
  80. Returns:
  81. A new component instance.
  82. """
  83. if color is not None:
  84. style = props.get("style", {})
  85. style["color"] = color
  86. props["style"] = style
  87. if color_scheme is not None:
  88. props["color"] = color_scheme
  89. component = super().create(*children, **props)
  90. if component.library is None:
  91. component.library = RadixThemesComponent.__fields__["library"].default
  92. component.alias = "RadixThemes" + (
  93. component.tag or component.__class__.__name__
  94. )
  95. return component
  96. @classmethod
  97. def get_fields(cls) -> dict[str, Any]:
  98. """Get the pydantic fields for the component.
  99. Returns:
  100. Mapping of field name to ModelField instance.
  101. """
  102. fields = super().get_fields()
  103. if "color_scheme" in fields:
  104. # Treat "color" as a direct prop, so the translation of reflex "color_scheme"
  105. # to "color" does not put the "color_scheme" value into the "style" prop.
  106. fields["color"] = fields.pop("color_scheme")
  107. fields["color"].required = False
  108. return fields
  109. @staticmethod
  110. def _get_app_wrap_components() -> dict[tuple[int, str], Component]:
  111. return {
  112. (45, "RadixThemesColorModeProvider"): RadixThemesColorModeProvider.create(),
  113. }
  114. class Theme(RadixThemesComponent):
  115. """A theme provider for radix components.
  116. This should be applied as `App.theme` to apply the theme to all radix
  117. components in the app with the given settings.
  118. It can also be used in a normal page to apply specified properties to all
  119. child elements as an override of the main theme.
  120. """
  121. tag = "Theme"
  122. # Whether to apply the themes background color to the theme node. Defaults to True.
  123. has_background: Var[bool]
  124. # Override light or dark mode theme: "inherit" | "light" | "dark". Defaults to "inherit".
  125. appearance: Var[LiteralAppearance]
  126. # The color used for default buttons, typography, backgrounds, etc
  127. accent_color: Var[LiteralAccentColor]
  128. # The shade of gray, defaults to "auto".
  129. gray_color: Var[LiteralGrayColor]
  130. # Whether panel backgrounds are translucent: "solid" | "translucent" (default)
  131. panel_background: Var[LiteralPanelBackground]
  132. # Element border radius: "none" | "small" | "medium" | "large" | "full". Defaults to "medium".
  133. radius: Var[LiteralRadius]
  134. # Scale of all theme items: "90%" | "95%" | "100%" | "105%" | "110%". Defaults to "100%"
  135. scaling: Var[LiteralScaling]
  136. def _get_imports(self) -> imports.ImportDict:
  137. return imports.merge_imports(
  138. super()._get_imports(),
  139. {
  140. "": [
  141. imports.ImportVar(tag="@radix-ui/themes/styles.css", install=False)
  142. ],
  143. "/utils/theme.js": [
  144. imports.ImportVar(tag="theme", is_default=True),
  145. ],
  146. },
  147. )
  148. def _render(self, props: dict[str, Any] | None = None) -> Tag:
  149. tag = super()._render(props)
  150. tag.add_props(
  151. css=Var.create(
  152. "{{...theme.styles.global[':root'], ...theme.styles.global.body}}",
  153. _var_is_local=False,
  154. ),
  155. )
  156. return tag
  157. class ThemePanel(RadixThemesComponent):
  158. """Visual editor for creating and editing themes.
  159. Include as a child component of Theme to use in your app.
  160. """
  161. tag = "ThemePanel"
  162. default_open: Var[bool]
  163. class RadixThemesColorModeProvider(Component):
  164. """Next-themes integration for radix themes components."""
  165. library = "/components/reflex/radix_themes_color_mode_provider.js"
  166. tag = "RadixThemesColorModeProvider"
  167. is_default = True