cronixui 1.1.1 → 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.
Files changed (76) hide show
  1. package/README.md +12 -8
  2. package/package.json +71 -71
  3. package/packages/flutter/.qwen/settings.json +7 -0
  4. package/packages/flutter/pubspec.yaml +20 -20
  5. package/packages/go/cronixui/cronixui.go +926 -926
  6. package/packages/python/README.md +142 -0
  7. package/packages/python/cronixui/__init__.py +15 -6
  8. package/packages/python/cronixui/__pycache__/__init__.cpython-314.pyc +0 -0
  9. package/packages/python/cronixui/__pycache__/accordion.cpython-314.pyc +0 -0
  10. package/packages/python/cronixui/__pycache__/alert.cpython-314.pyc +0 -0
  11. package/packages/python/cronixui/__pycache__/avatar.cpython-314.pyc +0 -0
  12. package/packages/python/cronixui/__pycache__/badge.cpython-314.pyc +0 -0
  13. package/packages/python/cronixui/__pycache__/button.cpython-314.pyc +0 -0
  14. package/packages/python/cronixui/__pycache__/card.cpython-314.pyc +0 -0
  15. package/packages/python/cronixui/__pycache__/command_palette.cpython-314.pyc +0 -0
  16. package/packages/python/cronixui/__pycache__/core.cpython-314.pyc +0 -0
  17. package/packages/python/cronixui/__pycache__/dropdown.cpython-314.pyc +0 -0
  18. package/packages/python/cronixui/__pycache__/form.cpython-314.pyc +0 -0
  19. package/packages/python/cronixui/__pycache__/layout.cpython-314.pyc +0 -0
  20. package/packages/python/cronixui/__pycache__/list.cpython-314.pyc +0 -0
  21. package/packages/python/cronixui/__pycache__/loading.cpython-314.pyc +0 -0
  22. package/packages/python/cronixui/__pycache__/modal.cpython-314.pyc +0 -0
  23. package/packages/python/cronixui/__pycache__/nav.cpython-314.pyc +0 -0
  24. package/packages/python/cronixui/__pycache__/pagination.cpython-314.pyc +0 -0
  25. package/packages/python/cronixui/__pycache__/progress.cpython-314.pyc +0 -0
  26. package/packages/python/cronixui/__pycache__/search.cpython-314.pyc +0 -0
  27. package/packages/python/cronixui/__pycache__/table.cpython-314.pyc +0 -0
  28. package/packages/python/cronixui/__pycache__/tabs.cpython-314.pyc +0 -0
  29. package/packages/python/cronixui/__pycache__/toast.cpython-314.pyc +0 -0
  30. package/packages/python/cronixui/__pycache__/toggle.cpython-314.pyc +0 -0
  31. package/packages/python/cronixui/__pycache__/tokens.cpython-314.pyc +0 -0
  32. package/packages/python/cronixui/__pycache__/tooltip.cpython-314.pyc +0 -0
  33. package/packages/python/cronixui/alert.py +119 -36
  34. package/packages/python/cronixui/avatar.py +129 -22
  35. package/packages/python/cronixui/badge.py +161 -24
  36. package/packages/python/cronixui/button.py +96 -27
  37. package/packages/python/cronixui/card.py +206 -33
  38. package/packages/python/cronixui/core.py +212 -23
  39. package/packages/python/cronixui/form.py +552 -141
  40. package/packages/python/cronixui/layout.py +358 -96
  41. package/packages/python/cronixui/list.py +140 -37
  42. package/packages/python/cronixui/loading.py +107 -17
  43. package/packages/python/cronixui/progress.py +189 -47
  44. package/packages/python/cronixui/table.py +118 -31
  45. package/packages/python/cronixui/tooltip.py +117 -15
  46. package/packages/react/src/components/Accordion.tsx +82 -82
  47. package/packages/react/src/components/Button.tsx +47 -47
  48. package/packages/react/src/components/Card.tsx +69 -69
  49. package/packages/react/src/components/CommandPalette.tsx +131 -131
  50. package/packages/react/src/components/Dropdown.tsx +88 -88
  51. package/packages/react/src/components/FileInput.tsx +86 -86
  52. package/packages/react/src/components/FormGroup.tsx +36 -36
  53. package/packages/react/src/components/List.tsx +55 -55
  54. package/packages/react/src/components/Pagination.tsx +107 -107
  55. package/packages/react/src/components/Progress.tsx +49 -49
  56. package/packages/react/src/components/Search.tsx +95 -95
  57. package/packages/react/src/components/Sidebar.tsx +64 -64
  58. package/packages/react/src/components/Stack.tsx +69 -69
  59. package/packages/react/src/components/Table.tsx +90 -90
  60. package/packages/react/src/components/Toast.tsx +134 -134
  61. package/packages/react/src/components/Typography.tsx +66 -66
  62. package/packages/react/src/index.ts +40 -40
  63. package/packages/react/src/styles.css +2039 -2039
  64. package/packages/rust/cronixui/src/components/avatar.rs +85 -85
  65. package/packages/rust/cronixui/src/components/breadcrumb.rs +58 -58
  66. package/packages/rust/cronixui/src/components/card.rs +259 -259
  67. package/packages/rust/cronixui/src/components/command_palette.rs +254 -254
  68. package/packages/rust/cronixui/src/components/dropdown.rs +179 -179
  69. package/packages/rust/cronixui/src/components/file_input.rs +74 -74
  70. package/packages/rust/cronixui/src/components/mod.rs +51 -51
  71. package/packages/rust/cronixui/src/components/search.rs +185 -185
  72. package/packages/rust/cronixui/src/components/skeleton.rs +63 -63
  73. package/packages/rust/cronixui/src/components/table.rs +56 -56
  74. package/packages/rust/cronixui/src/lib.rs +128 -128
  75. package/packages/web/dist/cronixui.css +97 -93
  76. package/packages/web/dist/cronixui.min.css +1 -1
@@ -1,143 +1,405 @@
1
- """CronixUI Layout Components"""
1
+ """CronixUI Layout Components.
2
2
 
3
- from .core import create_el
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
- class Header:
7
- """Header component."""
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
- brand_el = create_el("a", "cn-header-brand")
17
- brand_el.textContent = self.brand
18
- el.appendChild(brand_el)
13
+ @dataclass
14
+ class LayoutElement:
15
+ """Represents a rendered layout element."""
19
16
 
20
- self.nav = create_el("nav", "cn-header-nav")
21
- el.appendChild(self.nav)
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
- self.actions = create_el("div", "cn-header-actions")
24
- el.appendChild(self.actions)
22
+ def render_html(self) -> str:
23
+ """Render as HTML string.
25
24
 
26
- return el
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 add_nav_item(self, text: str, href: str = "#"):
29
- link = create_el("a", "cn-btn cn-btn-ghost")
30
- link.textContent = text
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
- class Sidebar:
39
- """Sidebar navigation component."""
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
- def __init__(self):
42
- self.element = self._render()
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
- self.header = create_el("div", "cn-sidebar-header")
48
- el.appendChild(self.header)
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("&", "&amp;")
128
+ .replace("<", "&lt;")
129
+ .replace(">", "&gt;")
130
+ .replace('"', "&quot;")
131
+ .replace("'", "&#x27;")
132
+ )
49
133
 
50
- self.nav = create_el("nav", "cn-sidebar-nav")
51
- el.appendChild(self.nav)
52
134
 
53
- self.footer = create_el("div", "cn-sidebar-footer")
54
- el.appendChild(self.footer)
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("&", "&amp;")
214
+ .replace("<", "&lt;")
215
+ .replace(">", "&gt;")
216
+ .replace('"', "&quot;")
217
+ .replace("'", "&#x27;")
218
+ )
55
219
 
56
- return el
57
220
 
58
- def add_item(
59
- self, text: str, icon: str = None, href: str = "#", active: bool = False
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
- item = create_el("a", "cn-sidebar-item")
62
- item.setAttribute("href", href)
63
- if active:
64
- item.classList.add("cn-sidebar-active")
65
-
66
- if icon:
67
- icon_el = create_el("span")
68
- icon_el.innerHTML = icon
69
- item.appendChild(icon_el)
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("&", "&amp;")
280
+ .replace("<", "&lt;")
281
+ .replace(">", "&gt;")
282
+ .replace('"', "&quot;")
283
+ .replace("'", "&#x27;")
284
+ )
70
285
 
71
- text_el = create_el("span")
72
- text_el.textContent = text
73
- item.appendChild(text_el)
74
286
 
75
- self.nav.appendChild(item)
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
- class Footer:
79
- """Footer component."""
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
- def __init__(self, copyright: str = ""):
82
- self.copyright = copyright
83
- self.element = self._render()
300
+ SIZES = ("sm", "md", "lg", "xl", "fluid")
84
301
 
85
- def _render(self):
86
- el = create_el("footer", "cn-footer")
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
- content = create_el("div", "cn-footer-content")
306
+ self.size = size
307
+ self.inner_html = inner_html
89
308
 
90
- self.links = create_el("div", "cn-footer-links")
91
- content.appendChild(self.links)
309
+ def render(self) -> LayoutElement:
310
+ """Render the container as a LayoutElement.
92
311
 
93
- copyright_el = create_el("div", "cn-footer-copyright")
94
- copyright_el.textContent = self.copyright
95
- content.appendChild(copyright_el)
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
- el.appendChild(content)
98
- return el
319
+ return LayoutElement(
320
+ classes=classes,
321
+ inner_html=self.inner_html,
322
+ )
99
323
 
100
- def add_link(self, text: str, href: str = "#"):
101
- link = create_el("a", "cn-footer-link")
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
- SIZES = ("sm", "md", "lg", "xl", "fluid")
333
+ class Divider:
334
+ """Divider component (horizontal rule).
111
335
 
112
- def __init__(self, size: str = "lg"):
113
- self.size = size if size in self.SIZES else "lg"
114
- self.element = self._render()
336
+ Example:
337
+ >>> divider = Divider()
338
+ >>> print(divider.render_html())
339
+ <div class="cn-divider" />
340
+ """
115
341
 
116
- def _render(self):
117
- classes = "cn-container"
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
- class Divider:
124
- """Divider component."""
352
+ def render_html(self) -> str:
353
+ """Render the divider as an HTML string.
125
354
 
126
- def __init__(self):
127
- self.element = create_el("div", "cn-divider")
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
- self.size = size if size in self.SIZES else "md"
137
- self.element = self._render()
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
- def _render(self):
140
- classes = "cn-section"
386
+ Returns:
387
+ LayoutElement representing the section
388
+ """
389
+ classes = ["cn-section"]
141
390
  if self.size != "md":
142
- classes += f" cn-section-{self.size}"
143
- return create_el("section", classes)
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()