120 lines
3.4 KiB
Python
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"
|