cronixui 1.1.2 → 1.1.3
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.
- package/README.md +1 -1
- package/package.json +71 -71
- package/packages/flutter/.qwen/settings.json +7 -0
- package/packages/flutter/pubspec.yaml +20 -20
- package/packages/go/cronixui/cronixui.go +926 -926
- package/packages/python/README.md +142 -0
- package/packages/python/cronixui/__init__.py +15 -6
- package/packages/python/cronixui/__pycache__/__init__.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/accordion.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/alert.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/avatar.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/badge.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/button.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/card.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/command_palette.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/core.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/dropdown.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/form.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/layout.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/list.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/loading.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/modal.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/nav.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/pagination.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/progress.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/search.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/table.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/tabs.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/toast.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/toggle.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/tokens.cpython-314.pyc +0 -0
- package/packages/python/cronixui/__pycache__/tooltip.cpython-314.pyc +0 -0
- package/packages/python/cronixui/alert.py +119 -36
- package/packages/python/cronixui/avatar.py +129 -22
- package/packages/python/cronixui/badge.py +161 -24
- package/packages/python/cronixui/button.py +96 -27
- package/packages/python/cronixui/card.py +206 -33
- package/packages/python/cronixui/core.py +212 -23
- package/packages/python/cronixui/form.py +552 -141
- package/packages/python/cronixui/layout.py +358 -96
- package/packages/python/cronixui/list.py +140 -37
- package/packages/python/cronixui/loading.py +107 -17
- package/packages/python/cronixui/progress.py +189 -47
- package/packages/python/cronixui/table.py +118 -31
- package/packages/python/cronixui/tooltip.py +117 -15
- package/packages/react/src/components/Accordion.tsx +82 -82
- package/packages/react/src/components/Button.tsx +47 -47
- package/packages/react/src/components/Card.tsx +69 -69
- package/packages/react/src/components/CommandPalette.tsx +131 -131
- package/packages/react/src/components/Dropdown.tsx +88 -88
- package/packages/react/src/components/FileInput.tsx +86 -86
- package/packages/react/src/components/FormGroup.tsx +36 -36
- package/packages/react/src/components/List.tsx +55 -55
- package/packages/react/src/components/Pagination.tsx +107 -107
- package/packages/react/src/components/Progress.tsx +49 -49
- package/packages/react/src/components/Search.tsx +95 -95
- package/packages/react/src/components/Sidebar.tsx +64 -64
- package/packages/react/src/components/Stack.tsx +69 -69
- package/packages/react/src/components/Table.tsx +90 -90
- package/packages/react/src/components/Toast.tsx +134 -134
- package/packages/react/src/components/Typography.tsx +66 -66
- package/packages/react/src/index.ts +40 -40
- package/packages/react/src/styles.css +2039 -2039
- package/packages/rust/cronixui/src/components/avatar.rs +85 -85
- package/packages/rust/cronixui/src/components/breadcrumb.rs +58 -58
- package/packages/rust/cronixui/src/components/card.rs +259 -259
- package/packages/rust/cronixui/src/components/command_palette.rs +254 -254
- package/packages/rust/cronixui/src/components/dropdown.rs +179 -179
- package/packages/rust/cronixui/src/components/file_input.rs +74 -74
- package/packages/rust/cronixui/src/components/mod.rs +51 -51
- package/packages/rust/cronixui/src/components/search.rs +185 -185
- package/packages/rust/cronixui/src/components/skeleton.rs +63 -63
- package/packages/rust/cronixui/src/components/table.rs +56 -56
- package/packages/rust/cronixui/src/lib.rs +128 -128
- package/packages/web/dist/cronixui.css +97 -93
- package/packages/web/dist/cronixui.min.css +1 -1
|
@@ -1,143 +1,405 @@
|
|
|
1
|
-
"""CronixUI Layout Components
|
|
1
|
+
"""CronixUI Layout Components.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Generates HTML for headers, sidebars, footers, containers, dividers, and sections.
|
|
4
|
+
No browser DOM APIs are used - all output is HTML strings or data structures.
|
|
5
|
+
"""
|
|
4
6
|
|
|
7
|
+
from __future__ import annotations
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def __init__(self, brand: str = ""):
|
|
10
|
-
self.brand = brand
|
|
11
|
-
self.element = self._render()
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Dict, List, Optional
|
|
12
11
|
|
|
13
|
-
def _render(self):
|
|
14
|
-
el = create_el("header", "cn-header")
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
@dataclass
|
|
14
|
+
class LayoutElement:
|
|
15
|
+
"""Represents a rendered layout element."""
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
tag: str = "div"
|
|
18
|
+
classes: List[str] = field(default_factory=list)
|
|
19
|
+
attributes: Dict[str, str] = field(default_factory=dict)
|
|
20
|
+
inner_html: str = ""
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
def render_html(self) -> str:
|
|
23
|
+
"""Render as HTML string.
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
Returns:
|
|
26
|
+
Complete HTML for the layout element
|
|
27
|
+
"""
|
|
28
|
+
class_str = " ".join(self.classes)
|
|
29
|
+
class_attr = f' class="{class_str}"' if class_str else ""
|
|
30
|
+
attrs_str = "".join(f' {k}="{v}"' for k, v in self.attributes.items())
|
|
31
|
+
return f"<{self.tag}{class_attr}{attrs_str}>{self.inner_html}</{self.tag}>"
|
|
27
32
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
link.setAttribute("href", href)
|
|
32
|
-
self.nav.appendChild(link)
|
|
33
|
+
def render(self) -> "LayoutElement":
|
|
34
|
+
"""Return self for API compatibility."""
|
|
35
|
+
return self
|
|
33
36
|
|
|
34
|
-
def add_action(self, element):
|
|
35
|
-
self.actions.appendChild(element)
|
|
36
37
|
|
|
38
|
+
@dataclass
|
|
39
|
+
class NavItem:
|
|
40
|
+
"""A navigation item for headers and sidebars.
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
Attributes:
|
|
43
|
+
text: Display text
|
|
44
|
+
href: Link URL (default: "#")
|
|
45
|
+
icon: Optional SVG icon markup
|
|
46
|
+
active: Whether item is highlighted
|
|
47
|
+
"""
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
49
|
+
text: str
|
|
50
|
+
href: str = "#"
|
|
51
|
+
icon: Optional[str] = None
|
|
52
|
+
active: bool = False
|
|
43
53
|
|
|
44
|
-
def _render(self):
|
|
45
|
-
el = create_el("aside", "cn-sidebar")
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
class Header:
|
|
56
|
+
"""Header component with brand, navigation, and action slots.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
brand: Brand/site name
|
|
60
|
+
nav_items: Optional list of NavItem instances
|
|
61
|
+
action_html: Optional HTML string for the actions slot
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
>>> header = Header(
|
|
65
|
+
... brand="MyApp",
|
|
66
|
+
... nav_items=[
|
|
67
|
+
... NavItem(text="Home", href="/"),
|
|
68
|
+
... NavItem(text="About", href="/about"),
|
|
69
|
+
... ],
|
|
70
|
+
... )
|
|
71
|
+
>>> print(header.render_html())
|
|
72
|
+
<header class="cn-header">
|
|
73
|
+
<a class="cn-header-brand">MyApp</a>
|
|
74
|
+
<nav class="cn-header-nav">
|
|
75
|
+
<a class="cn-btn cn-btn-ghost" href="/">Home</a>
|
|
76
|
+
<a class="cn-btn cn-btn-ghost" href="/about">About</a>
|
|
77
|
+
</nav>
|
|
78
|
+
<div class="cn-header-actions"></div>
|
|
79
|
+
</header>
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
brand: str = "",
|
|
85
|
+
nav_items: Optional[List[NavItem]] = None,
|
|
86
|
+
action_html: Optional[str] = None,
|
|
87
|
+
):
|
|
88
|
+
self.brand = brand
|
|
89
|
+
self.nav_items = nav_items or []
|
|
90
|
+
self.action_html = action_html or ""
|
|
91
|
+
|
|
92
|
+
def render(self) -> LayoutElement:
|
|
93
|
+
"""Render the header as a LayoutElement.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
LayoutElement representing the header
|
|
97
|
+
"""
|
|
98
|
+
parts = [f'<a class="cn-header-brand">{self._esc(self.brand)}</a>']
|
|
99
|
+
|
|
100
|
+
# Navigation
|
|
101
|
+
nav_items_html = "".join(
|
|
102
|
+
f'<a class="cn-btn cn-btn-ghost" href="{self._esc(item.href)}">{self._esc(item.text)}</a>'
|
|
103
|
+
for item in self.nav_items
|
|
104
|
+
)
|
|
105
|
+
parts.append(f'<nav class="cn-header-nav">{nav_items_html}</nav>')
|
|
106
|
+
|
|
107
|
+
# Actions
|
|
108
|
+
parts.append(f'<div class="cn-header-actions">{self.action_html}</div>')
|
|
109
|
+
|
|
110
|
+
return LayoutElement(
|
|
111
|
+
tag="header",
|
|
112
|
+
classes=["cn-header"],
|
|
113
|
+
inner_html="".join(parts),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def render_html(self) -> str:
|
|
117
|
+
"""Render the header as an HTML string.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
HTML string representation of the header
|
|
121
|
+
"""
|
|
122
|
+
return self.render().render_html()
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def _esc(text: str) -> str:
|
|
126
|
+
return (
|
|
127
|
+
text.replace("&", "&")
|
|
128
|
+
.replace("<", "<")
|
|
129
|
+
.replace(">", ">")
|
|
130
|
+
.replace('"', """)
|
|
131
|
+
.replace("'", "'")
|
|
132
|
+
)
|
|
49
133
|
|
|
50
|
-
self.nav = create_el("nav", "cn-sidebar-nav")
|
|
51
|
-
el.appendChild(self.nav)
|
|
52
134
|
|
|
53
|
-
|
|
54
|
-
|
|
135
|
+
class Sidebar:
|
|
136
|
+
"""Sidebar navigation component.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
items: Optional list of NavItem instances
|
|
140
|
+
header_html: Optional HTML for the sidebar header
|
|
141
|
+
footer_html: Optional HTML for the sidebar footer
|
|
142
|
+
|
|
143
|
+
Example:
|
|
144
|
+
>>> sidebar = Sidebar(
|
|
145
|
+
... items=[
|
|
146
|
+
... NavItem(text="Dashboard", icon="<svg>...</svg>", active=True),
|
|
147
|
+
... NavItem(text="Settings", icon="<svg>...</svg>"),
|
|
148
|
+
... ],
|
|
149
|
+
... )
|
|
150
|
+
>>> print(sidebar.render_html())
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
def __init__(
|
|
154
|
+
self,
|
|
155
|
+
items: Optional[List[NavItem]] = None,
|
|
156
|
+
header_html: Optional[str] = None,
|
|
157
|
+
footer_html: Optional[str] = None,
|
|
158
|
+
):
|
|
159
|
+
self.items = items or []
|
|
160
|
+
self.header_html = header_html or ""
|
|
161
|
+
self.footer_html = footer_html or ""
|
|
162
|
+
|
|
163
|
+
def render(self) -> LayoutElement:
|
|
164
|
+
"""Render the sidebar as a LayoutElement.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
LayoutElement representing the sidebar
|
|
168
|
+
"""
|
|
169
|
+
parts = []
|
|
170
|
+
|
|
171
|
+
# Header
|
|
172
|
+
if self.header_html:
|
|
173
|
+
parts.append(f'<div class="cn-sidebar-header">{self.header_html}</div>')
|
|
174
|
+
else:
|
|
175
|
+
parts.append('<div class="cn-sidebar-header"></div>')
|
|
176
|
+
|
|
177
|
+
# Nav items
|
|
178
|
+
nav_parts = []
|
|
179
|
+
for item in self.items:
|
|
180
|
+
active_class = " cn-sidebar-active" if item.active else ""
|
|
181
|
+
item_html = f'<a class="cn-sidebar-item{active_class}" href="{self._esc(item.href)}">'
|
|
182
|
+
if item.icon:
|
|
183
|
+
item_html += f'<span>{item.icon}</span>'
|
|
184
|
+
item_html += f'<span>{self._esc(item.text)}</span>'
|
|
185
|
+
item_html += "</a>"
|
|
186
|
+
nav_parts.append(item_html)
|
|
187
|
+
|
|
188
|
+
parts.append(f'<nav class="cn-sidebar-nav">{"".join(nav_parts)}</nav>')
|
|
189
|
+
|
|
190
|
+
# Footer
|
|
191
|
+
if self.footer_html:
|
|
192
|
+
parts.append(f'<div class="cn-sidebar-footer">{self.footer_html}</div>')
|
|
193
|
+
else:
|
|
194
|
+
parts.append('<div class="cn-sidebar-footer"></div>')
|
|
195
|
+
|
|
196
|
+
return LayoutElement(
|
|
197
|
+
tag="aside",
|
|
198
|
+
classes=["cn-sidebar"],
|
|
199
|
+
inner_html="".join(parts),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
def render_html(self) -> str:
|
|
203
|
+
"""Render the sidebar as an HTML string.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
HTML string representation of the sidebar
|
|
207
|
+
"""
|
|
208
|
+
return self.render().render_html()
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def _esc(text: str) -> str:
|
|
212
|
+
return (
|
|
213
|
+
text.replace("&", "&")
|
|
214
|
+
.replace("<", "<")
|
|
215
|
+
.replace(">", ">")
|
|
216
|
+
.replace('"', """)
|
|
217
|
+
.replace("'", "'")
|
|
218
|
+
)
|
|
55
219
|
|
|
56
|
-
return el
|
|
57
220
|
|
|
58
|
-
|
|
59
|
-
|
|
221
|
+
class Footer:
|
|
222
|
+
"""Footer component with links and copyright.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
copyright: Copyright text
|
|
226
|
+
links: Optional list of (text, href) tuples
|
|
227
|
+
|
|
228
|
+
Example:
|
|
229
|
+
>>> footer = Footer(
|
|
230
|
+
... copyright="© 2026 My Company",
|
|
231
|
+
... links=[("Privacy", "/privacy"), ("Terms", "/terms")],
|
|
232
|
+
... )
|
|
233
|
+
>>> print(footer.render_html())
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
def __init__(
|
|
237
|
+
self,
|
|
238
|
+
copyright: str = "",
|
|
239
|
+
links: Optional[List[tuple]] = None,
|
|
60
240
|
):
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
241
|
+
self.copyright = copyright
|
|
242
|
+
self.links = links or []
|
|
243
|
+
|
|
244
|
+
def render(self) -> LayoutElement:
|
|
245
|
+
"""Render the footer as a LayoutElement.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
LayoutElement representing the footer
|
|
249
|
+
"""
|
|
250
|
+
links_html = "".join(
|
|
251
|
+
f'<a class="cn-footer-link" href="{self._esc(href)}">{self._esc(text)}</a>'
|
|
252
|
+
for text, href in self.links
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
inner = (
|
|
256
|
+
f'<div class="cn-footer-content">'
|
|
257
|
+
f'<div class="cn-footer-links">{links_html}</div>'
|
|
258
|
+
f'<div class="cn-footer-copyright">{self._esc(self.copyright)}</div>'
|
|
259
|
+
f"</div>"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
return LayoutElement(
|
|
263
|
+
tag="footer",
|
|
264
|
+
classes=["cn-footer"],
|
|
265
|
+
inner_html=inner,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
def render_html(self) -> str:
|
|
269
|
+
"""Render the footer as an HTML string.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
HTML string representation of the footer
|
|
273
|
+
"""
|
|
274
|
+
return self.render().render_html()
|
|
275
|
+
|
|
276
|
+
@staticmethod
|
|
277
|
+
def _esc(text: str) -> str:
|
|
278
|
+
return (
|
|
279
|
+
text.replace("&", "&")
|
|
280
|
+
.replace("<", "<")
|
|
281
|
+
.replace(">", ">")
|
|
282
|
+
.replace('"', """)
|
|
283
|
+
.replace("'", "'")
|
|
284
|
+
)
|
|
70
285
|
|
|
71
|
-
text_el = create_el("span")
|
|
72
|
-
text_el.textContent = text
|
|
73
|
-
item.appendChild(text_el)
|
|
74
286
|
|
|
75
|
-
|
|
287
|
+
class Container:
|
|
288
|
+
"""Container component for constrained-width layouts.
|
|
76
289
|
|
|
290
|
+
Args:
|
|
291
|
+
size: Container size - sm, md, lg, xl, fluid (default: lg)
|
|
292
|
+
inner_html: Optional inner HTML content
|
|
77
293
|
|
|
78
|
-
|
|
79
|
-
|
|
294
|
+
Example:
|
|
295
|
+
>>> container = Container(size="xl", inner_html="<h1>Welcome</h1>")
|
|
296
|
+
>>> print(container.render_html())
|
|
297
|
+
<div class="cn-container cn-container-xl"><h1>Welcome</h1></div>
|
|
298
|
+
"""
|
|
80
299
|
|
|
81
|
-
|
|
82
|
-
self.copyright = copyright
|
|
83
|
-
self.element = self._render()
|
|
300
|
+
SIZES = ("sm", "md", "lg", "xl", "fluid")
|
|
84
301
|
|
|
85
|
-
def
|
|
86
|
-
|
|
302
|
+
def __init__(self, size: str = "lg", inner_html: str = ""):
|
|
303
|
+
if size not in self.SIZES:
|
|
304
|
+
raise ValueError(f"Invalid size '{size}'. Must be one of {self.SIZES}")
|
|
87
305
|
|
|
88
|
-
|
|
306
|
+
self.size = size
|
|
307
|
+
self.inner_html = inner_html
|
|
89
308
|
|
|
90
|
-
|
|
91
|
-
|
|
309
|
+
def render(self) -> LayoutElement:
|
|
310
|
+
"""Render the container as a LayoutElement.
|
|
92
311
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
312
|
+
Returns:
|
|
313
|
+
LayoutElement representing the container
|
|
314
|
+
"""
|
|
315
|
+
classes = ["cn-container"]
|
|
316
|
+
if self.size != "lg":
|
|
317
|
+
classes.append(f"cn-container-{self.size}")
|
|
96
318
|
|
|
97
|
-
|
|
98
|
-
|
|
319
|
+
return LayoutElement(
|
|
320
|
+
classes=classes,
|
|
321
|
+
inner_html=self.inner_html,
|
|
322
|
+
)
|
|
99
323
|
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
link.textContent = text
|
|
103
|
-
link.setAttribute("href", href)
|
|
104
|
-
self.links.appendChild(link)
|
|
324
|
+
def render_html(self) -> str:
|
|
325
|
+
"""Render the container as an HTML string.
|
|
105
326
|
|
|
327
|
+
Returns:
|
|
328
|
+
HTML string representation of the container
|
|
329
|
+
"""
|
|
330
|
+
return self.render().render_html()
|
|
106
331
|
|
|
107
|
-
class Container:
|
|
108
|
-
"""Container component."""
|
|
109
332
|
|
|
110
|
-
|
|
333
|
+
class Divider:
|
|
334
|
+
"""Divider component (horizontal rule).
|
|
111
335
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
336
|
+
Example:
|
|
337
|
+
>>> divider = Divider()
|
|
338
|
+
>>> print(divider.render_html())
|
|
339
|
+
<div class="cn-divider" />
|
|
340
|
+
"""
|
|
115
341
|
|
|
116
|
-
def
|
|
117
|
-
|
|
118
|
-
if self.size != "lg":
|
|
119
|
-
classes += f" cn-container-{self.size}"
|
|
120
|
-
return create_el("div", classes)
|
|
342
|
+
def render(self) -> LayoutElement:
|
|
343
|
+
"""Render the divider as a LayoutElement.
|
|
121
344
|
|
|
345
|
+
Returns:
|
|
346
|
+
LayoutElement representing the divider
|
|
347
|
+
"""
|
|
348
|
+
return LayoutElement(
|
|
349
|
+
classes=["cn-divider"],
|
|
350
|
+
)
|
|
122
351
|
|
|
123
|
-
|
|
124
|
-
|
|
352
|
+
def render_html(self) -> str:
|
|
353
|
+
"""Render the divider as an HTML string.
|
|
125
354
|
|
|
126
|
-
|
|
127
|
-
|
|
355
|
+
Returns:
|
|
356
|
+
HTML string representation of the divider
|
|
357
|
+
"""
|
|
358
|
+
return self.render().render_html()
|
|
128
359
|
|
|
129
360
|
|
|
130
361
|
class Section:
|
|
131
|
-
"""Section spacing component.
|
|
362
|
+
"""Section spacing component.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
size: Section size - sm, md, lg (default: md)
|
|
366
|
+
inner_html: Optional inner HTML content
|
|
367
|
+
|
|
368
|
+
Example:
|
|
369
|
+
>>> section = Section(size="lg", inner_html="<p>Section content</p>")
|
|
370
|
+
>>> print(section.render_html())
|
|
371
|
+
<section class="cn-section cn-section-lg"><p>Section content</p></section>
|
|
372
|
+
"""
|
|
132
373
|
|
|
133
374
|
SIZES = ("sm", "md", "lg")
|
|
134
375
|
|
|
135
|
-
def __init__(self, size: str = "md"):
|
|
136
|
-
|
|
137
|
-
|
|
376
|
+
def __init__(self, size: str = "md", inner_html: str = ""):
|
|
377
|
+
if size not in self.SIZES:
|
|
378
|
+
raise ValueError(f"Invalid size '{size}'. Must be one of {self.SIZES}")
|
|
379
|
+
|
|
380
|
+
self.size = size
|
|
381
|
+
self.inner_html = inner_html
|
|
382
|
+
|
|
383
|
+
def render(self) -> LayoutElement:
|
|
384
|
+
"""Render the section as a LayoutElement.
|
|
138
385
|
|
|
139
|
-
|
|
140
|
-
|
|
386
|
+
Returns:
|
|
387
|
+
LayoutElement representing the section
|
|
388
|
+
"""
|
|
389
|
+
classes = ["cn-section"]
|
|
141
390
|
if self.size != "md":
|
|
142
|
-
classes
|
|
143
|
-
|
|
391
|
+
classes.append(f"cn-section-{self.size}")
|
|
392
|
+
|
|
393
|
+
return LayoutElement(
|
|
394
|
+
tag="section",
|
|
395
|
+
classes=classes,
|
|
396
|
+
inner_html=self.inner_html,
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
def render_html(self) -> str:
|
|
400
|
+
"""Render the section as an HTML string.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
HTML string representation of the section
|
|
404
|
+
"""
|
|
405
|
+
return self.render().render_html()
|