htmui 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- htmui/__init__.py +1 -0
- htmui/basecoat/__init__.py +0 -0
- htmui/basecoat/accordion.py +76 -0
- htmui/basecoat/alert.py +44 -0
- htmui/basecoat/avatar.py +52 -0
- htmui/basecoat/badge.py +32 -0
- htmui/basecoat/breadcrumb.py +46 -0
- htmui/basecoat/button.py +36 -0
- htmui/basecoat/button_group.py +22 -0
- htmui/basecoat/card.py +32 -0
- htmui/basecoat/cdn.py +14 -0
- htmui/basecoat/chart.py +86 -0
- htmui/basecoat/codeblock.py +109 -0
- htmui/basecoat/combobox.py +190 -0
- htmui/basecoat/command.py +128 -0
- htmui/basecoat/dialog.py +165 -0
- htmui/basecoat/drawer.py +66 -0
- htmui/basecoat/dropdown_menu.py +59 -0
- htmui/basecoat/field.py +91 -0
- htmui/basecoat/input_group.py +49 -0
- htmui/basecoat/item.py +94 -0
- htmui/basecoat/menu.py +131 -0
- htmui/basecoat/pagination.py +60 -0
- htmui/basecoat/popover.py +69 -0
- htmui/basecoat/progress.py +57 -0
- htmui/basecoat/radio_group.py +83 -0
- htmui/basecoat/select.py +190 -0
- htmui/basecoat/separator.py +8 -0
- htmui/basecoat/sidebar.py +270 -0
- htmui/basecoat/slider.py +50 -0
- htmui/basecoat/spinner.py +43 -0
- htmui/basecoat/switch.py +50 -0
- htmui/basecoat/table.py +38 -0
- htmui/basecoat/tabs.py +109 -0
- htmui/basecoat/theme_switcher.py +58 -0
- htmui/basecoat/toast.py +97 -0
- htmui/basecoat/typing.py +21 -0
- htmui/cli.py +257 -0
- htmui-0.1.0.dist-info/METADATA +159 -0
- htmui-0.1.0.dist-info/RECORD +43 -0
- htmui-0.1.0.dist-info/WHEEL +4 -0
- htmui-0.1.0.dist-info/entry_points.txt +5 -0
- htmui-0.1.0.dist-info/licenses/LICENSE +21 -0
htmui/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from htmy import ComponentType, PropertyValue, SafeStr, XBool, html, join_classes
|
|
2
|
+
|
|
3
|
+
__version__ = "0.2.0"
|
|
4
|
+
__framework__ = "BasecoatUI"
|
|
5
|
+
__framework_version__ = "1"
|
|
6
|
+
__framework_url__ = "https://basecoatui.com/components/accordion/"
|
|
7
|
+
|
|
8
|
+
js = SafeStr(
|
|
9
|
+
'<script src="https://cdn.jsdelivr.net/npm/basecoat-css@1/dist/js/accordion.min.js" defer></script>'
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
accordion_icon = SafeStr(
|
|
13
|
+
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" '
|
|
14
|
+
'stroke="currentColor">'
|
|
15
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" /></svg>'
|
|
16
|
+
)
|
|
17
|
+
"""`chevron-down` icon from https://heroicons.com/."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def accordion_item(
|
|
21
|
+
*content: ComponentType,
|
|
22
|
+
summary: ComponentType,
|
|
23
|
+
open: bool = False,
|
|
24
|
+
disabled: bool = False,
|
|
25
|
+
icon: ComponentType | None = accordion_icon,
|
|
26
|
+
class_: str | None = None,
|
|
27
|
+
**kwargs: PropertyValue,
|
|
28
|
+
) -> ComponentType:
|
|
29
|
+
"""
|
|
30
|
+
Accordion item.
|
|
31
|
+
|
|
32
|
+
Arguments:
|
|
33
|
+
*content: The content of the accordion item.
|
|
34
|
+
summary: The summary (trigger) of the item.
|
|
35
|
+
open: Whether the item is open by default.
|
|
36
|
+
disabled: Whether the item is disabled.
|
|
37
|
+
icon: Optional trailing icon in the summary. Defaults to a chevron icon.
|
|
38
|
+
class_: Extra CSS classes for the root component.
|
|
39
|
+
**kwargs: Extra attributes for the root component.
|
|
40
|
+
"""
|
|
41
|
+
if open:
|
|
42
|
+
kwargs["open"] = XBool.true
|
|
43
|
+
if disabled:
|
|
44
|
+
kwargs["aria_disabled"] = "true"
|
|
45
|
+
|
|
46
|
+
return html.details(
|
|
47
|
+
html.summary(summary, icon),
|
|
48
|
+
html.section(*content),
|
|
49
|
+
class_=class_,
|
|
50
|
+
**kwargs,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def accordion(
|
|
55
|
+
*children: ComponentType,
|
|
56
|
+
multiple: bool = False,
|
|
57
|
+
class_: str | None = None,
|
|
58
|
+
**kwargs: PropertyValue,
|
|
59
|
+
) -> ComponentType:
|
|
60
|
+
"""
|
|
61
|
+
Accordion root. Children are usually created with `accordion_item()`.
|
|
62
|
+
|
|
63
|
+
Arguments:
|
|
64
|
+
*children: Accordion items.
|
|
65
|
+
multiple: Allow more than one item open at a time.
|
|
66
|
+
class_: Extra CSS classes for the root component.
|
|
67
|
+
**kwargs: Extra attributes for the root component.
|
|
68
|
+
"""
|
|
69
|
+
if multiple:
|
|
70
|
+
kwargs["data_multiple"] = ""
|
|
71
|
+
|
|
72
|
+
return html.section(
|
|
73
|
+
*children,
|
|
74
|
+
class_=join_classes("accordion", class_),
|
|
75
|
+
**kwargs,
|
|
76
|
+
)
|
htmui/basecoat/alert.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from htmy import ComponentType, PropertyValue, html, join_classes
|
|
2
|
+
|
|
3
|
+
__version__ = "0.2.0"
|
|
4
|
+
__framework__ = "BasecoatUI"
|
|
5
|
+
__framework_version__ = "1"
|
|
6
|
+
__framework_url__ = "https://basecoatui.com/components/alert/"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def alert(
|
|
10
|
+
*children: ComponentType,
|
|
11
|
+
title: ComponentType,
|
|
12
|
+
icon: ComponentType = None,
|
|
13
|
+
footer: ComponentType | None = None,
|
|
14
|
+
class_: str | None = None,
|
|
15
|
+
content_class: str | None = None,
|
|
16
|
+
title_class: str | None = None,
|
|
17
|
+
destructive: bool = False,
|
|
18
|
+
**kwargs: PropertyValue,
|
|
19
|
+
) -> ComponentType:
|
|
20
|
+
"""
|
|
21
|
+
Alert component with optional icon, description, and action region.
|
|
22
|
+
|
|
23
|
+
Arguments:
|
|
24
|
+
*children: Optional content below the title.
|
|
25
|
+
title: Alert title.
|
|
26
|
+
icon: Optional icon.
|
|
27
|
+
footer: Optional footer, typically an `html.footer(...)`.
|
|
28
|
+
class_: Extra CSS classes for the root element.
|
|
29
|
+
content_class: Extra CSS classes for the description section.
|
|
30
|
+
title_class: Extra CSS classes for the title.
|
|
31
|
+
destructive: Whether to use the destructive variant.
|
|
32
|
+
**kwargs: Extra attributes for the root element.
|
|
33
|
+
"""
|
|
34
|
+
if destructive:
|
|
35
|
+
kwargs["data_variant"] = "destructive"
|
|
36
|
+
|
|
37
|
+
return html.div(
|
|
38
|
+
icon,
|
|
39
|
+
html.h2(title, class_=title_class),
|
|
40
|
+
html.section(*children, class_=content_class) if len(children) > 0 else None,
|
|
41
|
+
footer,
|
|
42
|
+
class_=join_classes("alert", class_),
|
|
43
|
+
**kwargs,
|
|
44
|
+
)
|
htmui/basecoat/avatar.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import Literal, TypeAlias
|
|
2
|
+
|
|
3
|
+
from htmy import ComponentType, PropertyValue, html, join_classes
|
|
4
|
+
|
|
5
|
+
__version__ = "0.2.0"
|
|
6
|
+
__framework__ = "BasecoatUI"
|
|
7
|
+
__framework_version__ = "1"
|
|
8
|
+
__framework_url__ = "https://basecoatui.com/components/avatar/"
|
|
9
|
+
|
|
10
|
+
AvatarSize: TypeAlias = Literal["sm", "lg"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def avatar(
|
|
14
|
+
*children: ComponentType,
|
|
15
|
+
size: AvatarSize | None = None,
|
|
16
|
+
class_: str | None = None,
|
|
17
|
+
**kwargs: PropertyValue,
|
|
18
|
+
) -> ComponentType:
|
|
19
|
+
"""
|
|
20
|
+
Avatar component.
|
|
21
|
+
|
|
22
|
+
Arguments:
|
|
23
|
+
*children: Avatar content.
|
|
24
|
+
size: Avatar size.
|
|
25
|
+
class_: Extra CSS classes for the avatar.
|
|
26
|
+
**kwargs: Extra attributes for the avatar.
|
|
27
|
+
"""
|
|
28
|
+
if size is not None:
|
|
29
|
+
kwargs["data_size"] = size
|
|
30
|
+
|
|
31
|
+
return html.span(*children, class_=join_classes("avatar", class_), **kwargs)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def avatar_group(
|
|
35
|
+
*children: ComponentType,
|
|
36
|
+
count: ComponentType | str | None = None,
|
|
37
|
+
class_: str | None = None,
|
|
38
|
+
**kwargs: PropertyValue,
|
|
39
|
+
) -> ComponentType:
|
|
40
|
+
"""
|
|
41
|
+
Group of overlapping avatars with an optional trailing count indicator.
|
|
42
|
+
|
|
43
|
+
Arguments:
|
|
44
|
+
*children: Avatar components.
|
|
45
|
+
count: Content of the trailing `data-count` indicator.
|
|
46
|
+
class_: Extra CSS classes for the group.
|
|
47
|
+
**kwargs: Extra attributes for the root element.
|
|
48
|
+
"""
|
|
49
|
+
if count is not None:
|
|
50
|
+
children = (*children, html.span(count, data_count=""))
|
|
51
|
+
|
|
52
|
+
return html.div(*children, class_=join_classes("avatar-group", class_), **kwargs)
|
htmui/basecoat/badge.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Literal
|
|
4
|
+
|
|
5
|
+
from htmy import html
|
|
6
|
+
from htmy.utils import join
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from htmy import ComponentType, PropertyValue
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
__version__ = "0.2.0"
|
|
13
|
+
__framework__ = "BasecoatUI"
|
|
14
|
+
__framework_version__ = "1"
|
|
15
|
+
__framework_url__ = "https://basecoatui.com/components/badge/"
|
|
16
|
+
|
|
17
|
+
BadgeVariant = Literal["secondary", "destructive", "outline", "ghost"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def badge(
|
|
21
|
+
*children: ComponentType,
|
|
22
|
+
variant: BadgeVariant | None = None,
|
|
23
|
+
class_: str | None = None,
|
|
24
|
+
**kwargs: PropertyValue,
|
|
25
|
+
) -> ComponentType:
|
|
26
|
+
if variant is not None:
|
|
27
|
+
kwargs["data_variant"] = variant
|
|
28
|
+
return html.span(
|
|
29
|
+
*children,
|
|
30
|
+
class_=join("badge", class_),
|
|
31
|
+
**kwargs,
|
|
32
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from htmy import ComponentType, PropertyValue, SafeStr, html
|
|
4
|
+
from htmy.utils import join, join_components
|
|
5
|
+
|
|
6
|
+
__version__ = "0.2.0"
|
|
7
|
+
__framework__ = "BasecoatUI"
|
|
8
|
+
__framework_version__ = "1"
|
|
9
|
+
__framework_url__ = "https://basecoatui.com/components/breadcrumb/"
|
|
10
|
+
|
|
11
|
+
chevron_right_icon = SafeStr(
|
|
12
|
+
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" '
|
|
13
|
+
'stroke="currentColor" class="size-4"><path stroke-linecap="round" stroke-linejoin="round" '
|
|
14
|
+
'd="m8.25 4.5 7.5 7.5-7.5 7.5" /></svg>'
|
|
15
|
+
)
|
|
16
|
+
"""`chevron-right` icon from https://heroicons.com/ with small adjustments."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def breadcrumb(
|
|
20
|
+
*children: ComponentType,
|
|
21
|
+
separator: ComponentType = chevron_right_icon,
|
|
22
|
+
class_: str | None = None,
|
|
23
|
+
aria_label: str = "Breadcrumb",
|
|
24
|
+
**kwargs: PropertyValue,
|
|
25
|
+
) -> ComponentType:
|
|
26
|
+
"""
|
|
27
|
+
Breadcrumb navigation component.
|
|
28
|
+
|
|
29
|
+
Arguments:
|
|
30
|
+
*children: Breadcrumb items.
|
|
31
|
+
separator: Separator to be inserted between items.
|
|
32
|
+
class_: Extra CSS classes for the root element.
|
|
33
|
+
aria_label: Accessible name for the breadcrumb.
|
|
34
|
+
**kwargs: Extra attributes for the root element.
|
|
35
|
+
"""
|
|
36
|
+
return html.nav(
|
|
37
|
+
html.ol(
|
|
38
|
+
*join_components(
|
|
39
|
+
tuple(html.li(child) for child in children if child is not None),
|
|
40
|
+
html.li(separator, aria_hidden="true"),
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
class_=join("breadcrumb", class_),
|
|
44
|
+
aria_label=aria_label,
|
|
45
|
+
**kwargs,
|
|
46
|
+
)
|
htmui/basecoat/button.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from typing import Literal, TypeAlias
|
|
2
|
+
|
|
3
|
+
from htmy import ComponentType, PropertyValue, html, join_classes
|
|
4
|
+
|
|
5
|
+
__version__ = "0.2.0"
|
|
6
|
+
__framework__ = "BasecoatUI"
|
|
7
|
+
__framework_version__ = "1"
|
|
8
|
+
__framework_url__ = "https://basecoatui.com/components/button/"
|
|
9
|
+
|
|
10
|
+
ButtonVariant: TypeAlias = Literal["outline", "secondary", "ghost", "destructive", "link"]
|
|
11
|
+
|
|
12
|
+
ButtonSize: TypeAlias = Literal["xs", "sm", "default", "lg", "icon", "icon-xs", "icon-sm", "icon-lg"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def button(
|
|
16
|
+
*children: ComponentType,
|
|
17
|
+
variant: ButtonVariant | None = None,
|
|
18
|
+
size: ButtonSize | None = None,
|
|
19
|
+
class_: str | None = None,
|
|
20
|
+
**kwargs: PropertyValue,
|
|
21
|
+
) -> ComponentType:
|
|
22
|
+
"""
|
|
23
|
+
Button component.
|
|
24
|
+
|
|
25
|
+
Arguments:
|
|
26
|
+
*children: Button content.
|
|
27
|
+
variant: Button variant.
|
|
28
|
+
size: Button size.
|
|
29
|
+
class_: Extra CSS classes for the button.
|
|
30
|
+
**kwargs: Extra attributes for the button.
|
|
31
|
+
"""
|
|
32
|
+
if variant is not None:
|
|
33
|
+
kwargs["data_variant"] = variant
|
|
34
|
+
if size is not None:
|
|
35
|
+
kwargs["data_size"] = size
|
|
36
|
+
return html.button(*children, class_=join_classes("btn", class_), type_="button", **kwargs)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from htmy import ComponentType, PropertyValue, html
|
|
2
|
+
from htmy.utils import join
|
|
3
|
+
|
|
4
|
+
__version__ = "0.2.0"
|
|
5
|
+
__framework__ = "BasecoatUI"
|
|
6
|
+
__framework_version__ = "1"
|
|
7
|
+
__framework_url__ = "https://basecoatui.com/components/button-group/"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def button_group(
|
|
11
|
+
*children: ComponentType,
|
|
12
|
+
class_: str = "",
|
|
13
|
+
vertical: bool = False,
|
|
14
|
+
**kwargs: PropertyValue,
|
|
15
|
+
) -> ComponentType:
|
|
16
|
+
if "role" not in kwargs:
|
|
17
|
+
kwargs["role"] = "group"
|
|
18
|
+
|
|
19
|
+
if vertical:
|
|
20
|
+
kwargs["data-orientation"] = "vertical"
|
|
21
|
+
|
|
22
|
+
return html.div(*children, class_=join("button-group", class_), **kwargs)
|
htmui/basecoat/card.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from htmy import ComponentType, PropertyValue, html
|
|
4
|
+
from htmy.utils import join
|
|
5
|
+
|
|
6
|
+
__version__ = "0.2.0"
|
|
7
|
+
__framework__ = "BasecoatUI"
|
|
8
|
+
__framework_version__ = "1"
|
|
9
|
+
__framework_url__ = "https://basecoatui.com/components/card/"
|
|
10
|
+
|
|
11
|
+
CardSize = Literal["sm"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def card(
|
|
15
|
+
*children: ComponentType,
|
|
16
|
+
class_: str | None = None,
|
|
17
|
+
size: CardSize | None = None,
|
|
18
|
+
**kwargs: PropertyValue,
|
|
19
|
+
) -> ComponentType:
|
|
20
|
+
"""
|
|
21
|
+
Card container.
|
|
22
|
+
|
|
23
|
+
Arguments:
|
|
24
|
+
*children: Card content.
|
|
25
|
+
class_: Extra CSS classes for the root element.
|
|
26
|
+
size: Optional denser layout size.
|
|
27
|
+
**kwargs: Extra attributes for the root element.
|
|
28
|
+
"""
|
|
29
|
+
if size is not None:
|
|
30
|
+
kwargs["data_size"] = size
|
|
31
|
+
|
|
32
|
+
return html.div(*children, class_=join("card", class_), **kwargs)
|
htmui/basecoat/cdn.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from htmy import SafeStr
|
|
2
|
+
|
|
3
|
+
__version__ = "0.2.0"
|
|
4
|
+
__framework__ = "BasecoatUI"
|
|
5
|
+
__framework_version__ = "1"
|
|
6
|
+
__framework_url__ = "https://basecoatui.com"
|
|
7
|
+
|
|
8
|
+
css = SafeStr(
|
|
9
|
+
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/basecoat-css@1/dist/basecoat.cdn.min.css" />'
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
js = SafeStr('<script src="https://cdn.jsdelivr.net/npm/basecoat-css@1/dist/js/all.min.js" defer></script>')
|
|
13
|
+
|
|
14
|
+
tailwind_css = SafeStr('<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>')
|
htmui/basecoat/chart.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Protocol
|
|
3
|
+
|
|
4
|
+
from htmy import ComponentType, Fragment, PropertyValue, SafeStr, html
|
|
5
|
+
|
|
6
|
+
__version__ = "0.2.0"
|
|
7
|
+
__framework__ = "BasecoatUI"
|
|
8
|
+
__framework_version__ = "1"
|
|
9
|
+
__framework_url__ = "https://basecoatui.com/components/chart/"
|
|
10
|
+
|
|
11
|
+
js = SafeStr(
|
|
12
|
+
'<script src="https://cdn.jsdelivr.net/npm/basecoat-css@1/dist/js/chart.min.js" defer></script>'
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
chart_js = SafeStr('<script src="https://cdn.jsdelivr.net/npm/chart.js/dist/chart.umd.min.js"></script>')
|
|
16
|
+
"""Chart.js CDN script."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ChartInitScriptFactory(Protocol):
|
|
20
|
+
"""
|
|
21
|
+
Callable protocol for building a chart initialization script element
|
|
22
|
+
from a canvas ID and a chart config.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __call__(self, id: str, config: dict[str, Any], /) -> ComponentType:
|
|
26
|
+
"""
|
|
27
|
+
Returns the script element that initializes the chart for
|
|
28
|
+
the given ID with the given chart config.
|
|
29
|
+
"""
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class handle_chart_init:
|
|
34
|
+
@staticmethod
|
|
35
|
+
def default(id: str, config: dict[str, Any], /) -> ComponentType:
|
|
36
|
+
"""
|
|
37
|
+
Default `ChartInitScriptFactory` which initializes the chart once
|
|
38
|
+
the Basecoat runtime is ready.
|
|
39
|
+
"""
|
|
40
|
+
config_json = json.dumps(config, separators=(",", ":")).replace("</", "<\\/")
|
|
41
|
+
return html.script(
|
|
42
|
+
SafeStr(
|
|
43
|
+
f"""(() => {{
|
|
44
|
+
const canvas = document.getElementById({json.dumps(id)});
|
|
45
|
+
if (!canvas) return;
|
|
46
|
+
const init = () => window.basecoat?.chart?.(canvas, {config_json});
|
|
47
|
+
if (document.readyState === "loading") {{
|
|
48
|
+
document.addEventListener("DOMContentLoaded", init, {{ once: true }});
|
|
49
|
+
}} else {{
|
|
50
|
+
init();
|
|
51
|
+
}}
|
|
52
|
+
}})();"""
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def chart(
|
|
58
|
+
*,
|
|
59
|
+
id: str,
|
|
60
|
+
config: dict[str, Any] | None = None,
|
|
61
|
+
class_: str | None = None,
|
|
62
|
+
script: ChartInitScriptFactory | None = handle_chart_init.default,
|
|
63
|
+
**kwargs: PropertyValue,
|
|
64
|
+
) -> ComponentType:
|
|
65
|
+
"""
|
|
66
|
+
Chart.js canvas themed with Basecoat defaults.
|
|
67
|
+
|
|
68
|
+
Arguments:
|
|
69
|
+
id: Canvas element ID.
|
|
70
|
+
config: Optional JSON-serializable config for `basecoat.chart()`.
|
|
71
|
+
See the Basecoat Chart documentation for details.
|
|
72
|
+
class_: Extra CSS classes for the canvas element.
|
|
73
|
+
script: Optional callback that receives the canvas ID and `config` and
|
|
74
|
+
returns the initialization script element. Defaults to
|
|
75
|
+
`handle_chart_init.default`.
|
|
76
|
+
**kwargs: Extra attributes for the canvas element.
|
|
77
|
+
"""
|
|
78
|
+
canvas = html.canvas(
|
|
79
|
+
id=id,
|
|
80
|
+
class_=class_,
|
|
81
|
+
**kwargs,
|
|
82
|
+
)
|
|
83
|
+
if config is None or script is None:
|
|
84
|
+
return canvas
|
|
85
|
+
|
|
86
|
+
return Fragment(canvas, script(id, config))
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from htmy import ComponentType, SafeStr, html, join_classes
|
|
2
|
+
|
|
3
|
+
__version__ = "0.2.0"
|
|
4
|
+
__framework__ = "BasecoatUI"
|
|
5
|
+
__framework_version__ = "1"
|
|
6
|
+
__framework_url__ = "https://basecoatui.com/"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
copy_icon = SafeStr(
|
|
10
|
+
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" '
|
|
11
|
+
'stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" '
|
|
12
|
+
'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 '
|
|
13
|
+
"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 "
|
|
14
|
+
"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 "
|
|
15
|
+
"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 "
|
|
16
|
+
"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 "
|
|
17
|
+
'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>'
|
|
18
|
+
)
|
|
19
|
+
"""`clipboard-document` icon from https://heroicons.com/."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
copy_button_click_handler = SafeStr("""
|
|
23
|
+
const button = this;
|
|
24
|
+
const code = this.parentElement.querySelector('pre code');
|
|
25
|
+
if (!code) return;
|
|
26
|
+
const clickedClass = 'text-green-700';
|
|
27
|
+
const svg = this.parentElement.querySelector('svg');
|
|
28
|
+
navigator.clipboard.writeText(code.textContent || '').then(() => {
|
|
29
|
+
svg?.classList.add(clickedClass);
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
svg?.classList.remove(clickedClass);
|
|
32
|
+
}, 2000);
|
|
33
|
+
}).catch(err => {
|
|
34
|
+
console.error('Failed to copy text: ', err);
|
|
35
|
+
});""")
|
|
36
|
+
"""JavaScript for handling copy button click events."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def copy_button(icon: ComponentType = copy_icon) -> ComponentType:
|
|
40
|
+
return html.button(
|
|
41
|
+
icon,
|
|
42
|
+
onclick=copy_button_click_handler,
|
|
43
|
+
class_="btn size-8 absolute right-2.5 top-2 group hover:text-foreground text-muted-foreground",
|
|
44
|
+
data_variant="ghost",
|
|
45
|
+
data_size="icon",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def codeblock(code: str, *, code_class: str | None = None) -> ComponentType:
|
|
50
|
+
return html.pre(
|
|
51
|
+
html.code(code, class_=join_classes(code_class, "!bg-muted/40 !p-3.5")),
|
|
52
|
+
copy_button(),
|
|
53
|
+
class_="grid text-sm max-h-[650px] overflow-y-auto rounded-xl scrollbar relative",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_default_hljs_version = "11.11.1"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def highlightjs_theme(theme: str = "default", *, version: str = _default_hljs_version) -> SafeStr:
|
|
61
|
+
return SafeStr(
|
|
62
|
+
f'<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{version}/styles/{theme}.min.css">'
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def highlightjs_js(*, version: str = _default_hljs_version) -> SafeStr:
|
|
67
|
+
return SafeStr(
|
|
68
|
+
f'<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{version}/highlight.min.js"></script>'
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def highlightjs_languages(*languages: str, version: str = _default_hljs_version) -> SafeStr:
|
|
73
|
+
return SafeStr(
|
|
74
|
+
"\n".join(
|
|
75
|
+
(
|
|
76
|
+
f'<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{version}/languages/{lang}.min.js"></script>'
|
|
77
|
+
for lang in languages
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class highlightjs_events:
|
|
84
|
+
dom_content_loaded = "DOMContentLoaded"
|
|
85
|
+
htmx_after_swap = "htmx:after:swap"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def highlightjs_on_load(*events: str) -> SafeStr:
|
|
89
|
+
return SafeStr(
|
|
90
|
+
"\n".join(
|
|
91
|
+
(
|
|
92
|
+
"<script>",
|
|
93
|
+
"const highlight = () => {",
|
|
94
|
+
" if (!window.hljs) return;",
|
|
95
|
+
" document",
|
|
96
|
+
" .querySelectorAll(",
|
|
97
|
+
" 'pre code:not([data-highlighted]), code.highlight:not([data-highlighted])'",
|
|
98
|
+
")",
|
|
99
|
+
" .forEach(el => window.hljs.highlightElement(el));",
|
|
100
|
+
"};",
|
|
101
|
+
"",
|
|
102
|
+
"if (!window._hljsInit) {",
|
|
103
|
+
" window._hljsInit = true;",
|
|
104
|
+
*(f" document.addEventListener('{event}', highlight);" for event in events),
|
|
105
|
+
"}",
|
|
106
|
+
"</script>",
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
)
|