BasecoatUI components for htmy.

Code Block

Example:

def example() -> ComponentType:
    return codeblock(inspect.getsource(example), code_class="language-python")

Code example:

import inspect

from htmy import ComponentType

from htmui.basecoat.codeblock import codeblock


def example() -> ComponentType:
    return codeblock(inspect.getsource(example), code_class="language-python")

Component implementation:

For more details, see the BasecoatUI documentation.

from htmy import ComponentType, SafeStr, html, join_classes

__version__ = "0.1.0"
__framework__ = "BasecoatUI"
__framework_version__ = "0.3"
__framework_url__ = "https://basecoatui.com/"


copy_icon = SafeStr(
    '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" '
    'stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" '
    'd="M8.25 7.5V6.108c0-1.135.845-2.098 1.976-2.192.373-.03.748-.057 1.123-.08M15.75 18H18a2.25 '
    "2.25 0 0 0 2.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 0 0-1.123-.08M15.75 "
    "18.75v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5A3.375 3.375 "
    "0 0 0 6.375 7.5H5.25m11.9-3.664A2.251 2.251 0 0 0 15 2.25h-1.5a2.251 2.251 0 0 0-2.15 1.586m5.8 "
    "0c.065.21.1.433.1.664v.75h-6V4.5c0-.231.035-.454.1-.664M6.75 7.5H4.875c-.621 0-1.125.504-1.125 "
    '1.125v12c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V16.5a9 9 0 0 0-9-9Z" /></svg>'
)
"""`clipboard-document` icon from https://heroicons.com/."""


copy_button_click_handler = SafeStr("""
const button = this;
const code = this.parentElement.querySelector('pre code');
if (!code) return;
const clickedClass = 'text-green-700';
const svg = this.parentElement.querySelector('svg');
navigator.clipboard.writeText(code.textContent || '').then(() => {
  svg?.classList.add(clickedClass);
  setTimeout(() => {
    svg?.classList.remove(clickedClass);
  }, 2000);
}).catch(err => {
  console.error('Failed to copy text: ', err);
});""")
"""JavaScript for handling copy button click events."""


def copy_button(icon: ComponentType = copy_icon) -> ComponentType:
    return html.button(
        icon,
        onclick=copy_button_click_handler,
        class_=(
            "btn-icon-ghost size-8 absolute right-2.5 top-2 "
            "group hover:text-foreground text-muted-foreground"
        ),
    )


def codeblock(code: str, *, code_class: str | None = None) -> ComponentType:
    return html.pre(
        html.code(code, class_=join_classes(code_class, "!bg-muted/40 !p-3.5")),
        copy_button(),
        class_="grid text-sm max-h-[650px] overflow-y-auto rounded-xl scrollbar relative",
    )