reflex/pynecone/components/datadisplay/code.py
2023-06-23 13:01:57 -07:00

120 lines
3.4 KiB
Python

"""A code component."""
from typing import Dict
from pynecone.components.component import Component
from pynecone.components.forms import Button
from pynecone.components.layout import Box
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.components.media import Icon
from pynecone.event import set_clipboard
from pynecone.style import Style
from pynecone.utils import imports
from pynecone.vars import ImportVar, Var
# Path to the prism styles.
PRISM_STYLES_PATH = "/styles/code/prism"
class CodeBlock(Component):
"""A code block."""
library = "react-syntax-highlighter"
tag = "Prism"
# The theme to use ("light" or "dark").
theme: Var[str]
# The language to use.
language: Var[str]
# If this is enabled line numbers will be shown next to the code block.
show_line_numbers: Var[bool]
# The starting line number to use.
starting_line_number: Var[int]
# Whether to wrap long lines.
wrap_long_lines: Var[bool]
# A custom style for the code block.
custom_style: Var[Dict[str, str]]
# Props passed down to the code tag.
code_tag_props: Var[Dict[str, str]]
def _get_imports(self) -> imports.ImportDict:
merged_imports = super()._get_imports()
if self.theme is not None:
merged_imports = imports.merge_imports(
merged_imports, {PRISM_STYLES_PATH: {ImportVar(tag=self.theme.name)}}
)
return merged_imports
@classmethod
def create(cls, *children, can_copy=False, copy_button=None, **props):
"""Create a text component.
Args:
*children: The children of the component.
can_copy: Whether a copy button should appears.
copy_button: A custom copy button to override the default one.
**props: The props to pass to the component.
Returns:
The text component.
"""
# This component handles style in a special prop.
custom_style = props.pop("custom_style", {})
if can_copy:
code = children[0]
copy_button = (
copy_button
if copy_button is not None
else Button.create(
Icon.create(tag="copy"),
on_click=set_clipboard(code),
style={"position": "absolute", "top": "0.5em", "right": "0"},
)
)
custom_style.update({"padding": "1em 3.2em 1em 1em"})
else:
copy_button = None
# Transfer style props to the custom style prop.
for key, value in props.items():
if key not in cls.get_fields():
custom_style[key] = value
# Create the component.
code_block = super().create(
*children,
**props,
custom_style=Style(custom_style),
)
if copy_button:
return Box.create(code_block, copy_button, position="relative")
else:
return code_block
def _add_style(self, style):
self.custom_style = self.custom_style or {}
self.custom_style.update(style) # type: ignore
def _render(self):
out = super()._render()
if self.theme is not None:
out.add_props(
style=Var.create(self.theme.name, is_local=False)
).remove_props("theme")
return out
class Code(ChakraComponent):
"""Used to display inline code."""
tag = "Code"