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
|
@@ -0,0 +1,49 @@
|
|
|
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/input-group/"
|
|
9
|
+
|
|
10
|
+
AddonAlign: TypeAlias = Literal["start", "end"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def addon(
|
|
14
|
+
*children: ComponentType,
|
|
15
|
+
align: AddonAlign,
|
|
16
|
+
class_: str | None = None,
|
|
17
|
+
**kwargs: PropertyValue,
|
|
18
|
+
) -> ComponentType:
|
|
19
|
+
"""
|
|
20
|
+
Addon positioned at the start or end of an input group.
|
|
21
|
+
|
|
22
|
+
Arguments:
|
|
23
|
+
*children: Addon content.
|
|
24
|
+
align: On which side of the input the addon should be displayed.
|
|
25
|
+
class_: Extra CSS classes for the addon.
|
|
26
|
+
**kwargs: Extra attributes for the addon.
|
|
27
|
+
"""
|
|
28
|
+
return html.span(*children, class_=class_, data_align=align, **kwargs)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def input_group(
|
|
32
|
+
*children: ComponentType,
|
|
33
|
+
vertical: bool = False,
|
|
34
|
+
class_: str | None = None,
|
|
35
|
+
**kwargs: PropertyValue,
|
|
36
|
+
) -> ComponentType:
|
|
37
|
+
"""
|
|
38
|
+
Container that display controls and addons as an input group.
|
|
39
|
+
|
|
40
|
+
Arguments:
|
|
41
|
+
*children: Controls and addons contained by the group.
|
|
42
|
+
vertical: Whether to use a vertical layout.
|
|
43
|
+
class_: Extra CSS classes for the root element.
|
|
44
|
+
**kwargs: Extra attributes for the root element.
|
|
45
|
+
"""
|
|
46
|
+
if vertical:
|
|
47
|
+
kwargs["data_orientation"] = "vertical"
|
|
48
|
+
|
|
49
|
+
return html.div(*children, class_=join_classes("input-group", class_), **kwargs)
|
htmui/basecoat/item.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
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/item/"
|
|
9
|
+
|
|
10
|
+
ItemVariant: TypeAlias = Literal["outline", "muted"]
|
|
11
|
+
|
|
12
|
+
ItemSize: TypeAlias = Literal["xs", "sm"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def item(
|
|
16
|
+
*children: ComponentType,
|
|
17
|
+
variant: ItemVariant | None = None,
|
|
18
|
+
size: ItemSize | None = None,
|
|
19
|
+
class_: str | None = None,
|
|
20
|
+
**kwargs: PropertyValue,
|
|
21
|
+
) -> ComponentType:
|
|
22
|
+
"""
|
|
23
|
+
Item row.
|
|
24
|
+
|
|
25
|
+
Arguments:
|
|
26
|
+
*children: Item content.
|
|
27
|
+
variant: Item variant.
|
|
28
|
+
size: Item size.
|
|
29
|
+
class_: Extra CSS classes for the root element.
|
|
30
|
+
**kwargs: Extra attributes for the root element.
|
|
31
|
+
"""
|
|
32
|
+
if variant is not None:
|
|
33
|
+
kwargs["data_variant"] = variant
|
|
34
|
+
if size is not None:
|
|
35
|
+
kwargs["data_size"] = size
|
|
36
|
+
|
|
37
|
+
return html.article(
|
|
38
|
+
*children,
|
|
39
|
+
class_=join_classes("item", class_),
|
|
40
|
+
**kwargs,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def item_link(
|
|
45
|
+
*children: ComponentType,
|
|
46
|
+
href: str,
|
|
47
|
+
variant: ItemVariant | None = None,
|
|
48
|
+
size: ItemSize | None = None,
|
|
49
|
+
class_: str | None = None,
|
|
50
|
+
**kwargs: PropertyValue,
|
|
51
|
+
) -> ComponentType:
|
|
52
|
+
"""
|
|
53
|
+
Link-styled item row.
|
|
54
|
+
|
|
55
|
+
Arguments:
|
|
56
|
+
*children: Item content.
|
|
57
|
+
href: Destination URL.
|
|
58
|
+
variant: Item variant.
|
|
59
|
+
size: Item size.
|
|
60
|
+
class_: Extra CSS classes for the root element.
|
|
61
|
+
**kwargs: Extra attributes for the root element.
|
|
62
|
+
"""
|
|
63
|
+
if variant is not None:
|
|
64
|
+
kwargs["data_variant"] = variant
|
|
65
|
+
if size is not None:
|
|
66
|
+
kwargs["data_size"] = size
|
|
67
|
+
|
|
68
|
+
return html.a(
|
|
69
|
+
*children,
|
|
70
|
+
href=href,
|
|
71
|
+
class_=join_classes("item", class_),
|
|
72
|
+
**kwargs,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def item_group(
|
|
77
|
+
*children: ComponentType,
|
|
78
|
+
class_: str | None = None,
|
|
79
|
+
**kwargs: PropertyValue,
|
|
80
|
+
) -> ComponentType:
|
|
81
|
+
"""
|
|
82
|
+
Group of related items.
|
|
83
|
+
|
|
84
|
+
Arguments:
|
|
85
|
+
*children: The contained items.
|
|
86
|
+
class_: Extra CSS classes for the group.
|
|
87
|
+
**kwargs: Extra attributes for the root element.
|
|
88
|
+
"""
|
|
89
|
+
return html.div(
|
|
90
|
+
*children,
|
|
91
|
+
class_=join_classes("item-group", class_),
|
|
92
|
+
role="list",
|
|
93
|
+
**kwargs,
|
|
94
|
+
)
|
htmui/basecoat/menu.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from typing import Literal, TypeAlias
|
|
2
|
+
|
|
3
|
+
from htmy import ComponentType, Properties, PropertyValue, SafeStr, html
|
|
4
|
+
|
|
5
|
+
__version__ = "0.2.0"
|
|
6
|
+
__framework__ = "BasecoatUI"
|
|
7
|
+
__framework_version__ = "1"
|
|
8
|
+
__framework_url__ = "https://basecoatui.com/components/menu/"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
MenuItemKind: TypeAlias = Literal["item", "checkbox", "radio"]
|
|
12
|
+
ShortcutKind: TypeAlias = Literal["kbd", "command"]
|
|
13
|
+
|
|
14
|
+
check_icon = SafeStr(
|
|
15
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">'
|
|
16
|
+
'<path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 0 1 .143 1.052l-8 10.5'
|
|
17
|
+
"a.75.75 0 0 1-1.127.075l-4.5-4.5a.75.75 0 0 1 1.06-1.06l3.894 3.893 7.48-9.817"
|
|
18
|
+
'a.75.75 0 0 1 1.05-.143Z" clip-rule="evenodd" />'
|
|
19
|
+
"</svg>"
|
|
20
|
+
)
|
|
21
|
+
"""`check` icon from https://heroicons.com."""
|
|
22
|
+
|
|
23
|
+
_kind_has_indicator: set[MenuItemKind] = {"checkbox", "radio"}
|
|
24
|
+
|
|
25
|
+
_kind_to_role: dict[MenuItemKind, str] = {
|
|
26
|
+
"item": "menuitem",
|
|
27
|
+
"checkbox": "menuitemcheckbox",
|
|
28
|
+
"radio": "menuitemradio",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def menu(
|
|
33
|
+
*items: ComponentType,
|
|
34
|
+
id: str,
|
|
35
|
+
aria_labelledby: str | None = None,
|
|
36
|
+
aria_label: str | None = None,
|
|
37
|
+
class_: str | None = None,
|
|
38
|
+
**kwargs: PropertyValue,
|
|
39
|
+
) -> ComponentType:
|
|
40
|
+
"""Menu list wrapper."""
|
|
41
|
+
if aria_labelledby is not None:
|
|
42
|
+
kwargs["aria_labelledby"] = aria_labelledby
|
|
43
|
+
if aria_label is not None:
|
|
44
|
+
kwargs["aria_label"] = aria_label
|
|
45
|
+
return html.div(*items, id=id, class_=class_, role="menu", **kwargs)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def menu_item_group(
|
|
49
|
+
*items: ComponentType,
|
|
50
|
+
id: str,
|
|
51
|
+
label: str,
|
|
52
|
+
label_props: Properties | None = None,
|
|
53
|
+
) -> ComponentType:
|
|
54
|
+
"""Menu item group with a heading."""
|
|
55
|
+
label_id = f"{id}-label"
|
|
56
|
+
return html.div(
|
|
57
|
+
html.div(label, id=label_id, role="heading", **(label_props or {})),
|
|
58
|
+
*items,
|
|
59
|
+
id=id,
|
|
60
|
+
role="group",
|
|
61
|
+
aria_labelledby=label_id,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def menu_item( # noqa: C901
|
|
66
|
+
*children: ComponentType,
|
|
67
|
+
kind: MenuItemKind = "item",
|
|
68
|
+
class_: str | None = None,
|
|
69
|
+
disabled: bool = False,
|
|
70
|
+
checked: bool | None = None,
|
|
71
|
+
destructive: bool = False,
|
|
72
|
+
filter: str | None = None,
|
|
73
|
+
force: bool = False,
|
|
74
|
+
keep_open: bool = False,
|
|
75
|
+
keywords: str | None = None,
|
|
76
|
+
shortcut: str | None = None,
|
|
77
|
+
shortcut_kind: ShortcutKind = "kbd",
|
|
78
|
+
indicator: ComponentType | None = None,
|
|
79
|
+
**kwargs: PropertyValue,
|
|
80
|
+
) -> ComponentType:
|
|
81
|
+
"""
|
|
82
|
+
Menu item.
|
|
83
|
+
|
|
84
|
+
Works inside `command`, `dropdown_menu`, or other `role="menu"` components.
|
|
85
|
+
"""
|
|
86
|
+
role = _kind_to_role[kind]
|
|
87
|
+
|
|
88
|
+
if disabled:
|
|
89
|
+
kwargs["aria_disabled"] = "true"
|
|
90
|
+
|
|
91
|
+
if checked is not None:
|
|
92
|
+
kwargs["aria_checked"] = "true" if checked else "false"
|
|
93
|
+
elif kind in _kind_has_indicator:
|
|
94
|
+
kwargs["aria_checked"] = "false"
|
|
95
|
+
|
|
96
|
+
if destructive:
|
|
97
|
+
kwargs["data_variant"] = "destructive"
|
|
98
|
+
|
|
99
|
+
if filter is not None:
|
|
100
|
+
kwargs["data_filter"] = filter
|
|
101
|
+
|
|
102
|
+
if keywords is not None:
|
|
103
|
+
kwargs["data_keywords"] = keywords
|
|
104
|
+
|
|
105
|
+
if force:
|
|
106
|
+
kwargs["data_force"] = ""
|
|
107
|
+
|
|
108
|
+
if keep_open:
|
|
109
|
+
kwargs["data_keep_command_open"] = ""
|
|
110
|
+
|
|
111
|
+
if class_ is not None:
|
|
112
|
+
kwargs["class_"] = class_
|
|
113
|
+
|
|
114
|
+
indicator_comp: ComponentType | None = indicator
|
|
115
|
+
if indicator_comp is None and kind in _kind_has_indicator:
|
|
116
|
+
indicator_comp = check_icon
|
|
117
|
+
|
|
118
|
+
shortcut_comp: ComponentType | None = None
|
|
119
|
+
if shortcut is not None:
|
|
120
|
+
if shortcut_kind == "command":
|
|
121
|
+
shortcut_comp = html.span(shortcut, data_shortcut="")
|
|
122
|
+
else:
|
|
123
|
+
shortcut_comp = html.kbd(shortcut)
|
|
124
|
+
|
|
125
|
+
return html.div(
|
|
126
|
+
None if indicator_comp is None else html.span(indicator_comp, data_indicator=""),
|
|
127
|
+
*children,
|
|
128
|
+
shortcut_comp,
|
|
129
|
+
role=role,
|
|
130
|
+
**kwargs,
|
|
131
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
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/components/pagination/"
|
|
7
|
+
|
|
8
|
+
chevron_left_icon = SafeStr(
|
|
9
|
+
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" '
|
|
10
|
+
'stroke="currentColor" class="size-5"><path stroke-linecap="round" stroke-linejoin="round" '
|
|
11
|
+
'd="M15.75 19.5 8.25 12l7.5-7.5" /></svg>'
|
|
12
|
+
)
|
|
13
|
+
"""`chevron-left` icon from https://heroicons.com/."""
|
|
14
|
+
|
|
15
|
+
chevron_right_icon = SafeStr(
|
|
16
|
+
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" '
|
|
17
|
+
'stroke="currentColor" class="size-5"><path stroke-linecap="round" stroke-linejoin="round" '
|
|
18
|
+
'd="m8.25 4.5 7.5 7.5-7.5 7.5" /></svg>'
|
|
19
|
+
)
|
|
20
|
+
"""`chevron-right` icon from https://heroicons.com/."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def pagination_item(
|
|
24
|
+
*content: ComponentType,
|
|
25
|
+
url: str,
|
|
26
|
+
class_: str | None = None,
|
|
27
|
+
selected: bool = False,
|
|
28
|
+
) -> ComponentType:
|
|
29
|
+
return html.li(
|
|
30
|
+
html.a(
|
|
31
|
+
*content,
|
|
32
|
+
href=url,
|
|
33
|
+
class_=join_classes("btn", class_),
|
|
34
|
+
data_variant="outline" if selected else "ghost",
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def pagination(
|
|
40
|
+
*items: ComponentType,
|
|
41
|
+
next_content: ComponentType = SafeStr("Next"), # noqa: B008
|
|
42
|
+
next_url: str,
|
|
43
|
+
previous_content: ComponentType = SafeStr("Previous"), # noqa: B008
|
|
44
|
+
previous_url: str,
|
|
45
|
+
) -> ComponentType:
|
|
46
|
+
return html.nav(
|
|
47
|
+
html.ul(
|
|
48
|
+
None
|
|
49
|
+
if previous_content is None
|
|
50
|
+
else pagination_item(chevron_left_icon, previous_content, url=previous_url),
|
|
51
|
+
*items,
|
|
52
|
+
None
|
|
53
|
+
if next_content is None
|
|
54
|
+
else pagination_item(next_content, chevron_right_icon, url=next_url),
|
|
55
|
+
class_="flex flex-row items-center gap-1",
|
|
56
|
+
),
|
|
57
|
+
role="navigation",
|
|
58
|
+
aria_label="pagination",
|
|
59
|
+
class_="mx-auto flex w-full justify-center",
|
|
60
|
+
)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from htmy import ComponentType, PropertyValue, SafeStr, html, join_classes
|
|
2
|
+
|
|
3
|
+
from .button import ButtonVariant
|
|
4
|
+
from .typing import Align, FloatingSide
|
|
5
|
+
|
|
6
|
+
__version__ = "0.2.0"
|
|
7
|
+
__framework__ = "BasecoatUI"
|
|
8
|
+
__framework_version__ = "1"
|
|
9
|
+
__framework_url__ = "https://basecoatui.com/components/popover/"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
js = SafeStr(
|
|
13
|
+
'<script src="https://cdn.jsdelivr.net/npm/basecoat-css@1/dist/js/popover.min.js" defer></script>'
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def popover(
|
|
18
|
+
*children: ComponentType,
|
|
19
|
+
id: str,
|
|
20
|
+
button_content: ComponentType,
|
|
21
|
+
button_class: str | None = None,
|
|
22
|
+
button_variant: ButtonVariant | None = "outline",
|
|
23
|
+
class_: str | None = None,
|
|
24
|
+
popover_align: Align | None = None,
|
|
25
|
+
popover_class: str | None = "w-72",
|
|
26
|
+
popover_side: FloatingSide | None = None,
|
|
27
|
+
**kwargs: PropertyValue,
|
|
28
|
+
) -> ComponentType:
|
|
29
|
+
"""
|
|
30
|
+
Popover with a trigger button and inline-positioned content.
|
|
31
|
+
|
|
32
|
+
Arguments:
|
|
33
|
+
*children: Popover content.
|
|
34
|
+
id: Root element ID.
|
|
35
|
+
button_content: Trigger button content.
|
|
36
|
+
button_class: Extra CSS classes for the trigger button.
|
|
37
|
+
button_variant: Trigger button variant.
|
|
38
|
+
class_: Extra CSS classes for the root element.
|
|
39
|
+
popover_align: Popover content alignment.
|
|
40
|
+
popover_class: CSS classes for the popover content.
|
|
41
|
+
popover_side: Preferred popover side.
|
|
42
|
+
**kwargs: Extra attributes for the root element.
|
|
43
|
+
"""
|
|
44
|
+
trigger_id = f"{id}-trigger"
|
|
45
|
+
content_id = f"{id}-popover"
|
|
46
|
+
|
|
47
|
+
return html.div(
|
|
48
|
+
html.button(
|
|
49
|
+
button_content,
|
|
50
|
+
id=trigger_id,
|
|
51
|
+
type="button",
|
|
52
|
+
aria_controls=content_id,
|
|
53
|
+
aria_expanded="false",
|
|
54
|
+
class_=join_classes("btn", button_class),
|
|
55
|
+
data_variant=button_variant,
|
|
56
|
+
),
|
|
57
|
+
html.div(
|
|
58
|
+
*children,
|
|
59
|
+
id=content_id,
|
|
60
|
+
data_popover="",
|
|
61
|
+
aria_hidden="true",
|
|
62
|
+
class_=popover_class,
|
|
63
|
+
data_align=popover_align,
|
|
64
|
+
data_side=popover_side,
|
|
65
|
+
),
|
|
66
|
+
id=id,
|
|
67
|
+
class_=join_classes("popover", class_),
|
|
68
|
+
**kwargs,
|
|
69
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
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/progress/"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def progress(
|
|
10
|
+
*,
|
|
11
|
+
value: int,
|
|
12
|
+
min: int = 0,
|
|
13
|
+
max: int = 100,
|
|
14
|
+
start_label: ComponentType | None = None,
|
|
15
|
+
middle_label: ComponentType | None = None,
|
|
16
|
+
end_label: ComponentType | None = None,
|
|
17
|
+
class_: str | None = None,
|
|
18
|
+
**kwargs: PropertyValue,
|
|
19
|
+
) -> ComponentType:
|
|
20
|
+
"""
|
|
21
|
+
Progress bar component, with optional labels.
|
|
22
|
+
|
|
23
|
+
Arguments:
|
|
24
|
+
value: Current value, clamped to the `[min, max]` interval.
|
|
25
|
+
min: Minimum value.
|
|
26
|
+
max: Maximum value.
|
|
27
|
+
start_label: Optional label shown at the start of a row above the bar.
|
|
28
|
+
middle_label: Optional label shown in the middle of the row above the bar.
|
|
29
|
+
end_label: Optional label shown at the end of the row above the bar.
|
|
30
|
+
class_: Extra CSS classes for the root element.
|
|
31
|
+
**kwargs: Extra attributes for the root element.
|
|
32
|
+
"""
|
|
33
|
+
value = min if value < min else max if value > max else value
|
|
34
|
+
pct = (value - min) / (max - min) * 100 if max != min else 0
|
|
35
|
+
bar = html.div(
|
|
36
|
+
html.span(style=f"width: {pct:.4g}%"),
|
|
37
|
+
role="progressbar",
|
|
38
|
+
aria_valuenow=value,
|
|
39
|
+
aria_valuemin=min,
|
|
40
|
+
aria_valuemax=max,
|
|
41
|
+
class_=join_classes("progress", class_),
|
|
42
|
+
**kwargs,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if start_label is None and middle_label is None and end_label is None:
|
|
46
|
+
return bar
|
|
47
|
+
|
|
48
|
+
return html.div(
|
|
49
|
+
html.div(
|
|
50
|
+
start_label if start_label is not None else html.label(),
|
|
51
|
+
middle_label if middle_label is not None else html.label(),
|
|
52
|
+
end_label if end_label is not None else html.label(),
|
|
53
|
+
class_="flex items-center justify-between gap-2",
|
|
54
|
+
),
|
|
55
|
+
bar,
|
|
56
|
+
class_="grid w-full gap-1",
|
|
57
|
+
)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from htmy import ComponentType, PropertyValue, XBool, html, join_classes
|
|
2
|
+
|
|
3
|
+
__version__ = "0.2.0"
|
|
4
|
+
__framework__ = "BasecoatUI"
|
|
5
|
+
__framework_version__ = "1"
|
|
6
|
+
__framework_url__ = "https://basecoatui.com/components/radio-group/"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def radio_input(
|
|
10
|
+
*children: ComponentType,
|
|
11
|
+
name: str,
|
|
12
|
+
value: str,
|
|
13
|
+
description: ComponentType | None = None,
|
|
14
|
+
checked: bool = False,
|
|
15
|
+
disabled: bool = False,
|
|
16
|
+
invalid: bool = False,
|
|
17
|
+
class_: str | None = None,
|
|
18
|
+
**kwargs: PropertyValue,
|
|
19
|
+
) -> ComponentType:
|
|
20
|
+
"""
|
|
21
|
+
Radio option, a horizontal field with input and label.
|
|
22
|
+
|
|
23
|
+
Arguments:
|
|
24
|
+
*children: Label content.
|
|
25
|
+
name: The name of the input.
|
|
26
|
+
value: Value submitted when this option is selected.
|
|
27
|
+
description: Optional description shown under the label.
|
|
28
|
+
checked: Whether the option is initially selected.
|
|
29
|
+
disabled: Whether the option is disabled.
|
|
30
|
+
invalid: Whether to mark the option as invalid.
|
|
31
|
+
class_: Extra CSS classes for the field wrapper.
|
|
32
|
+
**kwargs: Extra attributes for the input element.
|
|
33
|
+
"""
|
|
34
|
+
input_id = f"{name}-{value}"
|
|
35
|
+
|
|
36
|
+
if checked:
|
|
37
|
+
kwargs["checked"] = XBool.true
|
|
38
|
+
if disabled:
|
|
39
|
+
kwargs["disabled"] = XBool.true
|
|
40
|
+
if invalid:
|
|
41
|
+
kwargs["aria_invalid"] = "true"
|
|
42
|
+
|
|
43
|
+
label = html.label(*children, for_=input_id)
|
|
44
|
+
content: ComponentType = html.section(label, html.p(description)) if description is not None else label
|
|
45
|
+
|
|
46
|
+
field_attrs: dict[str, PropertyValue] = {
|
|
47
|
+
"role": "group",
|
|
48
|
+
"data_orientation": "horizontal",
|
|
49
|
+
}
|
|
50
|
+
if disabled:
|
|
51
|
+
field_attrs["data_disabled"] = "true"
|
|
52
|
+
if invalid:
|
|
53
|
+
field_attrs["data_invalid"] = "true"
|
|
54
|
+
|
|
55
|
+
return html.div(
|
|
56
|
+
html.input_(
|
|
57
|
+
type="radio",
|
|
58
|
+
id=input_id,
|
|
59
|
+
name=name,
|
|
60
|
+
value=value,
|
|
61
|
+
class_="input",
|
|
62
|
+
**kwargs,
|
|
63
|
+
),
|
|
64
|
+
content,
|
|
65
|
+
class_=join_classes("field", class_),
|
|
66
|
+
**field_attrs,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def radio_group(
|
|
71
|
+
*children: ComponentType,
|
|
72
|
+
class_: str | None = None,
|
|
73
|
+
**kwargs: PropertyValue,
|
|
74
|
+
) -> ComponentType:
|
|
75
|
+
"""
|
|
76
|
+
Radio group container.
|
|
77
|
+
|
|
78
|
+
Arguments:
|
|
79
|
+
*children: The components in the group.
|
|
80
|
+
class_: Extra CSS classes for the root element.
|
|
81
|
+
**kwargs: Extra attributes for the root element.
|
|
82
|
+
"""
|
|
83
|
+
return html.div(*children, class_=class_, role="radiogroup", data_slot="radio-group", **kwargs)
|