Skip to content

Commit f871ba5

Browse files
committed
Added a button component and gif
1 parent 71e188a commit f871ba5

File tree

10 files changed

+686
-335
lines changed

10 files changed

+686
-335
lines changed

README.md

Lines changed: 219 additions & 137 deletions
Large diffs are not rendered by default.

bootstrap_exercise.py

Lines changed: 213 additions & 129 deletions
Large diffs are not rendered by default.

docs/images/pyhtml_example.gif

1.79 MB
Loading

py_html/contrib/bootstrap/avatar.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from py_html.contrib.bootstrap._types import BVariants
55
from py_html.contrib.bootstrap.icon import BIcon
66
from py_html.contrib.bootstrap.util import apply_classes
7-
from py_html.el import Element, Fragment
8-
from py_html.el.base import BuildContext, Component, NodeContext
7+
from py_html.el import Fragment
8+
from py_html.el.base import NodeContext
99
from py_html.styles import StyleCSS
1010

1111
avatar_style = el.Style(
@@ -30,7 +30,7 @@
3030
)
3131

3232

33-
class BAvatar(el.BaseElement, Component):
33+
class BAvatar(el.BaseElement):
3434
class_name = "b-avatar"
3535

3636
def __init__(
@@ -46,7 +46,6 @@ def __init__(
4646
rounded: t.Literal["circle", "square"] = "circle",
4747
icon: t.Optional[str] = None,
4848
text: t.Optional[str] = None,
49-
content: t.Optional[t.Any] = None,
5049
**attrs,
5150
):
5251
self.tag = tag
@@ -58,17 +57,13 @@ def __init__(
5857
self.badge_variant = badge_variant
5958
self.rounded = rounded
6059
self.size = size
61-
self.slot = content
6260

6361
if not self.icon and not self.text:
6462
self.icon = "people-fill"
6563

6664
super().__init__(**attrs)
6765

68-
def exports(self, ctx: BuildContext) -> None:
69-
ctx.add_root_style(avatar_style)
70-
71-
def resolve_content(self) -> t.Union[Fragment, Element]:
66+
def render_content(self, content: t.Any, ctx: NodeContext) -> str:
7267
self.class_name = (
7368
self.class_name
7469
+ " "
@@ -78,7 +73,7 @@ def resolve_content(self) -> t.Union[Fragment, Element]:
7873
if self.size:
7974
self.style.update_style(width=self.size, height=self.size)
8075

81-
return el.Fragment(
76+
element = el.Fragment(
8277
BAvatarText(
8378
content=self.text,
8479
style=StyleCSS(font_size=f"calc({self.size} * 0.4)")
@@ -106,8 +101,9 @@ def resolve_content(self) -> t.Union[Fragment, Element]:
106101
)
107102
if self.badge
108103
else el.Comment(),
109-
self.slot,
104+
content,
110105
)
106+
return ctx.render_content(element)
111107

112108

113109
class BAvatarText(el.Span):
@@ -203,7 +199,7 @@ def get_parent_style(self) -> StyleCSS:
203199
return StyleCSS()
204200

205201
def render_content(self, content: Fragment, ctx: NodeContext) -> str:
206-
for node in content:
202+
for node in content or []:
207203
if isinstance(node.element, BAvatar):
208204
if self.size:
209205
node.element.size = self.size
@@ -219,7 +215,7 @@ def render_content(self, content: Fragment, ctx: NodeContext) -> str:
219215
if self.variant:
220216
node.element.variant = self.variant
221217

222-
return ctx.get_content(content)
218+
return ctx.render_content(content)
223219

224220
def render_attributes(self, ctx: NodeContext) -> str:
225221
self.style = StyleCSS(**(self.style or {}), **self.get_parent_style())

py_html/contrib/bootstrap/badge.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import py_html.el as el
44
from py_html.contrib.bootstrap._types import BVariants
55
from py_html.contrib.bootstrap.util import apply_classes
6+
from py_html.el.base import NodeContext
67

78

89
class BBadge(el.BaseHTML):
@@ -13,11 +14,15 @@ def __init__(
1314
tag: str = "span",
1415
variant: t.Optional[BVariants] = "secondary",
1516
pill: bool = False,
16-
class_name: t.Optional[str] = "",
1717
**attrs,
1818
):
1919
self.tag = tag
2020
self.variant = variant
21+
self.pill = pill
2122

22-
class_name = class_name or "" + apply_classes(bg=variant, badge_pill=pill)
23-
super().__init__(class_name=class_name, **attrs)
23+
super().__init__(**attrs)
24+
25+
def render_attributes(self, ctx: NodeContext) -> str:
26+
self.class_name += "" + apply_classes(bg=self.variant, badge_pill=self.pill)
27+
28+
return super().render_attributes(ctx)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import typing as t
2+
3+
import py_html.el as el
4+
from py_html.contrib.bootstrap.util import apply_classes
5+
from py_html.el.base import NodeContext
6+
7+
aTarget = t.Literal[
8+
"_blank",
9+
"_self",
10+
"_parent",
11+
"_top",
12+
]
13+
14+
15+
class BreadcrumbItem(t.TypedDict, total=False):
16+
text: str
17+
href: t.Optional[str]
18+
active: bool
19+
target: aTarget
20+
21+
22+
class BBreadcrumb(el.Ol):
23+
class_name = "breadcrumb"
24+
25+
def __init__(self, *items: BreadcrumbItem, **attrs) -> None:
26+
super().__init__(**attrs)
27+
self.items = items
28+
if self.items and self.content:
29+
raise Exception("Invalid Configuration. Use 'Items' or 'Content'")
30+
31+
def render_content(self, content: t.Any, ctx: NodeContext) -> t.Any:
32+
if self.items:
33+
return ctx.render_content(self._get_breadcrumb_item_elements())
34+
return ctx.render_content(content)
35+
36+
def _get_breadcrumb_item_elements(self) -> el.Fragment:
37+
return el.Fragment(
38+
*(
39+
BBreadcrumbItem(
40+
href=item.get("href", "#"),
41+
active=item.get("active", False),
42+
target=item.get("target", "_self"),
43+
content=item["text"],
44+
)
45+
for item in self.items
46+
)
47+
)
48+
49+
50+
class BBreadcrumbItem(el.Li):
51+
class_name = "breadcrumb-item"
52+
53+
def __init__(
54+
self, target: aTarget = "_self", active: bool = False, href: str = "#", **attrs
55+
) -> None:
56+
attrs.setdefault("aria_current", "location")
57+
super().__init__(**attrs)
58+
59+
self.href = href
60+
self.target = target
61+
self.active = active
62+
63+
self.class_name += apply_classes(active=active)
64+
65+
def render_content(self, content: t.Any, ctx: NodeContext) -> t.Any:
66+
if self.active:
67+
return ctx.render_content(el.Span(content=content))
68+
69+
return ctx.render_content(
70+
el.A(href=self.href, content=content, target=self.target)
71+
)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import typing as t
2+
3+
import py_html.el as el
4+
from py_html.contrib.bootstrap.util import apply_classes
5+
from py_html.el.base import NodeContext
6+
7+
button_variant = t.Literal[
8+
"link",
9+
"primary",
10+
"secondary",
11+
"success",
12+
"danger",
13+
"warning",
14+
"info",
15+
"light",
16+
"dark",
17+
"outline-primary",
18+
"outline-secondary",
19+
"outline-success",
20+
"outline-danger",
21+
"outline-warning",
22+
"outline-info",
23+
"outline-light",
24+
"outline-dark",
25+
]
26+
27+
28+
class BButton(el.BaseHTML):
29+
class_name = "btn"
30+
31+
def __init__(
32+
self,
33+
tag: str = "button",
34+
disabled: bool = False,
35+
pill: bool = False,
36+
squared: bool = False,
37+
pressed: bool = False,
38+
size: t.Literal["sm", "md", "lg"] = "sm",
39+
type: t.Literal["button", "submit", "reset"] = "button",
40+
variant: button_variant = "primary",
41+
href: t.Optional[str] = None,
42+
**attrs,
43+
) -> None:
44+
self.tag = tag
45+
self.href = href
46+
self.pill = pill
47+
self.squared = squared
48+
self.size = size
49+
self.variant = variant
50+
self.type = type
51+
self.disabled = disabled
52+
self.pressed = pressed
53+
54+
super().__init__(**attrs)
55+
56+
def render_attributes(self, ctx: NodeContext) -> str:
57+
self.class_name += apply_classes(
58+
pill=self.pill,
59+
rounded="0" if self.squared else None,
60+
btn=self.variant,
61+
disabled=self.disabled,
62+
active=self.pressed,
63+
)
64+
self.class_name += apply_classes(
65+
btn=self.size,
66+
)
67+
if self.pressed:
68+
self.attrs.setdefault("aria_pressed", self.pressed)
69+
70+
self.attrs.update(disabled=self.disabled, type=self.type)
71+
return super().render_attributes(ctx)
72+
73+
74+
class BButtonGroup(el.BaseHTML):
75+
class_name = "btn-group"
76+
77+
def __init__(
78+
self,
79+
tag: str = "div",
80+
squared: bool = False,
81+
size: t.Literal["sm", "md", "lg"] = "sm",
82+
vertical: bool = False,
83+
**attrs,
84+
) -> None:
85+
attrs.setdefault("role", "group")
86+
87+
self.tag = tag
88+
self.vertical = vertical
89+
self.size = size
90+
self.squared = squared
91+
92+
super().__init__(**attrs)
93+
94+
if self.vertical:
95+
self.class_name.replace(self.__class__.class_name, "btn-group-vertical")
96+
97+
def render_content(self, content: t.Any, ctx: NodeContext) -> t.Any:
98+
for node in content or []:
99+
if isinstance(node.element, BButton):
100+
if self.size:
101+
node.element.size = self.size
102+
103+
if self.squared:
104+
node.element.squared = self.squared
105+
106+
return ctx.render_content(content)
107+
108+
109+
class BButtonToolbar(el.BaseHTML):
110+
class_name = "btn-toolbar"
111+
role = "toolbar"
112+
113+
def __init__(self, tag: str = "div", **attrs) -> None:
114+
self.tag = tag
115+
super().__init__(**attrs)

0 commit comments

Comments
 (0)