validate decorations dict
This commit is contained in:
parent
6a67e8f417
commit
ccaf2c65d4
@ -12,6 +12,7 @@ from reflex.components.core.colors import color
|
|||||||
from reflex.components.core.cond import color_mode_cond
|
from reflex.components.core.cond import color_mode_cond
|
||||||
from reflex.components.el.elements.forms import Button
|
from reflex.components.el.elements.forms import Button
|
||||||
from reflex.components.lucide.icon import Icon
|
from reflex.components.lucide.icon import Icon
|
||||||
|
from reflex.components.props import NoExtrasAllowedProps
|
||||||
from reflex.components.radix.themes.layout.box import Box
|
from reflex.components.radix.themes.layout.box import Box
|
||||||
from reflex.event import call_script, set_clipboard
|
from reflex.event import call_script, set_clipboard
|
||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
@ -390,6 +391,23 @@ LiteralCodeTheme = Literal[
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Position(NoExtrasAllowedProps):
|
||||||
|
"""Position of the decoration."""
|
||||||
|
|
||||||
|
line: str
|
||||||
|
character: str
|
||||||
|
|
||||||
|
|
||||||
|
class ShikiDecorations(NoExtrasAllowedProps):
|
||||||
|
"""Decorations for the code block."""
|
||||||
|
|
||||||
|
start: Union[int, Position]
|
||||||
|
end: Union[int, Position]
|
||||||
|
tag_name: str = "span"
|
||||||
|
properties: dict[str, Any] = {}
|
||||||
|
always_wrap: bool = False
|
||||||
|
|
||||||
|
|
||||||
class ShikiBaseTransformers(Base):
|
class ShikiBaseTransformers(Base):
|
||||||
"""Base for creating transformers."""
|
"""Base for creating transformers."""
|
||||||
|
|
||||||
@ -538,7 +556,7 @@ class ShikiCodeBlock(Component):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# The decorations to use for the syntax highlighter.
|
# The decorations to use for the syntax highlighter.
|
||||||
decorations: Var[list[dict[str, Any]]] = Var.create([])
|
decorations: Var[list[ShikiDecorations]] = Var.create([])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(
|
def create(
|
||||||
@ -558,6 +576,7 @@ class ShikiCodeBlock(Component):
|
|||||||
# Separate props for the code block and the wrapper
|
# Separate props for the code block and the wrapper
|
||||||
code_block_props = {}
|
code_block_props = {}
|
||||||
code_wrapper_props = {}
|
code_wrapper_props = {}
|
||||||
|
decorations = props.pop("decorations", [])
|
||||||
|
|
||||||
class_props = cls.get_props()
|
class_props = cls.get_props()
|
||||||
|
|
||||||
@ -577,6 +596,9 @@ class ShikiCodeBlock(Component):
|
|||||||
transformer_styles.update(transformer.style)
|
transformer_styles.update(transformer.style)
|
||||||
transformer_styles.update(code_wrapper_props.pop("style", {}))
|
transformer_styles.update(code_wrapper_props.pop("style", {}))
|
||||||
|
|
||||||
|
decorations = [ShikiDecorations(**decoration) for decoration in decorations]
|
||||||
|
code_block_props["decorations"] = decorations
|
||||||
|
|
||||||
return Box.create(
|
return Box.create(
|
||||||
code_block,
|
code_block,
|
||||||
*children[1:],
|
*children[1:],
|
||||||
|
@ -7,6 +7,7 @@ from typing import Any, Dict, Literal, Optional, Union, overload
|
|||||||
|
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.components.component import Component, ComponentNamespace
|
from reflex.components.component import Component, ComponentNamespace
|
||||||
|
from reflex.components.props import NoExtrasAllowedProps
|
||||||
from reflex.event import EventType
|
from reflex.event import EventType
|
||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
from reflex.vars.base import Var
|
from reflex.vars.base import Var
|
||||||
@ -328,6 +329,17 @@ LiteralCodeTheme = Literal[
|
|||||||
"vitesse-light",
|
"vitesse-light",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
class Position(NoExtrasAllowedProps):
|
||||||
|
line: str
|
||||||
|
character: str
|
||||||
|
|
||||||
|
class ShikiDecorations(NoExtrasAllowedProps):
|
||||||
|
start: Union[int, Position]
|
||||||
|
end: Union[int, Position]
|
||||||
|
tag_name: str
|
||||||
|
properties: dict[str, Any]
|
||||||
|
always_wrap: bool
|
||||||
|
|
||||||
class ShikiBaseTransformers(Base):
|
class ShikiBaseTransformers(Base):
|
||||||
library: str
|
library: str
|
||||||
fns: list[FunctionStringVar]
|
fns: list[FunctionStringVar]
|
||||||
@ -907,7 +919,7 @@ class ShikiCodeBlock(Component):
|
|||||||
]
|
]
|
||||||
] = None,
|
] = None,
|
||||||
decorations: Optional[
|
decorations: Optional[
|
||||||
Union[Var[list[dict[str, Any]]], list[dict[str, Any]]]
|
Union[Var[list[ShikiDecorations]], list[ShikiDecorations]]
|
||||||
] = None,
|
] = None,
|
||||||
style: Optional[Style] = None,
|
style: Optional[Style] = None,
|
||||||
key: Optional[Any] = None,
|
key: Optional[Any] = None,
|
||||||
@ -1534,7 +1546,7 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
|
|||||||
]
|
]
|
||||||
] = None,
|
] = None,
|
||||||
decorations: Optional[
|
decorations: Optional[
|
||||||
Union[Var[list[dict[str, Any]]], list[dict[str, Any]]]
|
Union[Var[list[ShikiDecorations]], list[ShikiDecorations]]
|
||||||
] = None,
|
] = None,
|
||||||
style: Optional[Style] = None,
|
style: Optional[Style] = None,
|
||||||
key: Optional[Any] = None,
|
key: Optional[Any] = None,
|
||||||
@ -2164,7 +2176,7 @@ class CodeblockNamespace(ComponentNamespace):
|
|||||||
]
|
]
|
||||||
] = None,
|
] = None,
|
||||||
decorations: Optional[
|
decorations: Optional[
|
||||||
Union[Var[list[dict[str, Any]]], list[dict[str, Any]]]
|
Union[Var[list[ShikiDecorations]], list[ShikiDecorations]]
|
||||||
] = None,
|
] = None,
|
||||||
style: Optional[Style] = None,
|
style: Optional[Style] = None,
|
||||||
key: Optional[Any] = None,
|
key: Optional[Any] = None,
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.utils import format
|
from reflex.utils import format
|
||||||
|
from reflex.utils.exceptions import InvalidPropValueError
|
||||||
from reflex.vars.object import LiteralObjectVar
|
from reflex.vars.object import LiteralObjectVar
|
||||||
|
|
||||||
|
|
||||||
@ -40,3 +43,34 @@ class PropsBase(Base):
|
|||||||
format.to_camel_case(key): value
|
format.to_camel_case(key): value
|
||||||
for key, value in super().dict(*args, **kwargs).items()
|
for key, value in super().dict(*args, **kwargs).items()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NoExtrasAllowedProps(Base):
|
||||||
|
"""A class that holds props to be passed or applied to a component with no extra props allowed."""
|
||||||
|
|
||||||
|
def __init__(self, component_name=None, **kwargs):
|
||||||
|
"""Initialize the props.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
component_name: The custom name of the component.
|
||||||
|
kwargs: Kwargs to initialize the props.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
InvalidPropValueError: If invalid props are passed on instantiation.
|
||||||
|
"""
|
||||||
|
component_name = component_name or type(self).__name__
|
||||||
|
try:
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
except ValidationError as e:
|
||||||
|
invalid_fields = ", ".join([error["loc"][0] for error in e.errors()]) # type: ignore
|
||||||
|
supported_props_str = ", ".join(f'"{field}"' for field in self.get_fields())
|
||||||
|
raise InvalidPropValueError(
|
||||||
|
f"Invalid prop(s) {invalid_fields} for {component_name!r}. Supported props are {supported_props_str}"
|
||||||
|
) from None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""Pydantic config."""
|
||||||
|
|
||||||
|
arbitrary_types_allowed = True
|
||||||
|
use_enum_values = True
|
||||||
|
extra = "forbid"
|
||||||
|
@ -4,12 +4,10 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any, ClassVar, Literal, Optional, Union
|
from typing import Any, ClassVar, Literal, Optional, Union
|
||||||
|
|
||||||
from pydantic import ValidationError
|
|
||||||
|
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.components.component import Component, ComponentNamespace
|
from reflex.components.component import Component, ComponentNamespace
|
||||||
from reflex.components.lucide.icon import Icon
|
from reflex.components.lucide.icon import Icon
|
||||||
from reflex.components.props import PropsBase
|
from reflex.components.props import NoExtrasAllowedProps, PropsBase
|
||||||
from reflex.event import (
|
from reflex.event import (
|
||||||
EventSpec,
|
EventSpec,
|
||||||
call_script,
|
call_script,
|
||||||
@ -72,7 +70,7 @@ def _toast_callback_signature(toast: Var) -> list[Var]:
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ToastProps(PropsBase):
|
class ToastProps(PropsBase, NoExtrasAllowedProps):
|
||||||
"""Props for the toast component."""
|
"""Props for the toast component."""
|
||||||
|
|
||||||
# Toast's title, renders above the description.
|
# Toast's title, renders above the description.
|
||||||
@ -132,24 +130,6 @@ class ToastProps(PropsBase):
|
|||||||
# Function that gets called when the toast disappears automatically after it's timeout (duration` prop).
|
# Function that gets called when the toast disappears automatically after it's timeout (duration` prop).
|
||||||
on_auto_close: Optional[Any]
|
on_auto_close: Optional[Any]
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
"""Initialize the props.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
kwargs: Kwargs to initialize the props.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If invalid props are passed on instantiation.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
except ValidationError as e:
|
|
||||||
invalid_fields = ", ".join([error["loc"][0] for error in e.errors()]) # type: ignore
|
|
||||||
supported_props_str = ", ".join(f'"{field}"' for field in self.get_fields())
|
|
||||||
raise ValueError(
|
|
||||||
f"Invalid prop(s) {invalid_fields} for rx.toast. Supported props are {supported_props_str}"
|
|
||||||
) from None
|
|
||||||
|
|
||||||
def dict(self, *args, **kwargs) -> dict[str, Any]:
|
def dict(self, *args, **kwargs) -> dict[str, Any]:
|
||||||
"""Convert the object to a dictionary.
|
"""Convert the object to a dictionary.
|
||||||
|
|
||||||
@ -181,13 +161,6 @@ class ToastProps(PropsBase):
|
|||||||
)
|
)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
class Config:
|
|
||||||
"""Pydantic config."""
|
|
||||||
|
|
||||||
arbitrary_types_allowed = True
|
|
||||||
use_enum_values = True
|
|
||||||
extra = "forbid"
|
|
||||||
|
|
||||||
|
|
||||||
class Toaster(Component):
|
class Toaster(Component):
|
||||||
"""A Toaster Component for displaying toast notifications."""
|
"""A Toaster Component for displaying toast notifications."""
|
||||||
@ -281,7 +254,7 @@ class Toaster(Component):
|
|||||||
if message == "" and ("title" not in props or "description" not in props):
|
if message == "" and ("title" not in props or "description" not in props):
|
||||||
raise ValueError("Toast message or title or description must be provided.")
|
raise ValueError("Toast message or title or description must be provided.")
|
||||||
if props:
|
if props:
|
||||||
args = LiteralVar.create(ToastProps(**props))
|
args = LiteralVar.create(ToastProps(component_name="rx.toast", **props))
|
||||||
toast = f"{toast_command}(`{message}`, {str(args)})"
|
toast = f"{toast_command}(`{message}`, {str(args)})"
|
||||||
else:
|
else:
|
||||||
toast = f"{toast_command}(`{message}`)"
|
toast = f"{toast_command}(`{message}`)"
|
||||||
|
@ -8,7 +8,7 @@ from typing import Any, ClassVar, Dict, Literal, Optional, Union, overload
|
|||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.components.component import Component, ComponentNamespace
|
from reflex.components.component import Component, ComponentNamespace
|
||||||
from reflex.components.lucide.icon import Icon
|
from reflex.components.lucide.icon import Icon
|
||||||
from reflex.components.props import PropsBase
|
from reflex.components.props import NoExtrasAllowedProps, PropsBase
|
||||||
from reflex.event import EventSpec, EventType
|
from reflex.event import EventSpec, EventType
|
||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
from reflex.utils.serializers import serializer
|
from reflex.utils.serializers import serializer
|
||||||
@ -31,7 +31,7 @@ class ToastAction(Base):
|
|||||||
@serializer
|
@serializer
|
||||||
def serialize_action(action: ToastAction) -> dict: ...
|
def serialize_action(action: ToastAction) -> dict: ...
|
||||||
|
|
||||||
class ToastProps(PropsBase):
|
class ToastProps(PropsBase, NoExtrasAllowedProps):
|
||||||
title: Optional[Union[str, Var]]
|
title: Optional[Union[str, Var]]
|
||||||
description: Optional[Union[str, Var]]
|
description: Optional[Union[str, Var]]
|
||||||
close_button: Optional[bool]
|
close_button: Optional[bool]
|
||||||
@ -52,11 +52,6 @@ class ToastProps(PropsBase):
|
|||||||
|
|
||||||
def dict(self, *args, **kwargs) -> dict[str, Any]: ...
|
def dict(self, *args, **kwargs) -> dict[str, Any]: ...
|
||||||
|
|
||||||
class Config:
|
|
||||||
arbitrary_types_allowed = True
|
|
||||||
use_enum_values = True
|
|
||||||
extra = "forbid"
|
|
||||||
|
|
||||||
class Toaster(Component):
|
class Toaster(Component):
|
||||||
is_used: ClassVar[bool] = False
|
is_used: ClassVar[bool] = False
|
||||||
|
|
||||||
|
@ -143,3 +143,7 @@ class EnvironmentVarValueError(ReflexError, ValueError):
|
|||||||
|
|
||||||
class DynamicComponentInvalidSignature(ReflexError, TypeError):
|
class DynamicComponentInvalidSignature(ReflexError, TypeError):
|
||||||
"""Raised when a dynamic component has an invalid signature."""
|
"""Raised when a dynamic component has an invalid signature."""
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidPropValueError(ReflexError, ValueError):
|
||||||
|
"""Raised when a prop value is invalid."""
|
||||||
|
Loading…
Reference in New Issue
Block a user