eidosui 0.4.0__tar.gz → 0.5.0__tar.gz
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.
- eidosui-0.5.0/PKG-INFO +113 -0
- eidosui-0.5.0/README.md +77 -0
- eidosui-0.5.0/eidos/__init__.py +270 -0
- eidosui-0.5.0/eidos/components/__init__.py +10 -0
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/components/headers.py +16 -14
- eidosui-0.5.0/eidos/components/navigation.py +87 -0
- eidosui-0.5.0/eidos/components/table.py +84 -0
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/css/styles.css +48 -0
- eidosui-0.5.0/eidos/plugins/__init__.py +1 -0
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/plugins/markdown/__init__.py +3 -3
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/plugins/markdown/components.py +10 -22
- eidosui-0.5.0/eidos/plugins/markdown/extensions/__init__.py +1 -0
- eidosui-0.5.0/eidos/plugins/markdown/extensions/alerts.py +124 -0
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/plugins/markdown/renderer.py +19 -24
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/styles.py +33 -9
- eidosui-0.5.0/eidos/tags.py +435 -0
- eidosui-0.5.0/eidos/utils.py +76 -0
- eidosui-0.5.0/pyproject.toml +163 -0
- eidosui-0.4.0/PKG-INFO +0 -127
- eidosui-0.4.0/README.md +0 -91
- eidosui-0.4.0/eidos/__init__.py +0 -0
- eidosui-0.4.0/eidos/components/navigation.py +0 -78
- eidosui-0.4.0/eidos/plugins/__init__.py +0 -1
- eidosui-0.4.0/eidos/plugins/markdown/extensions/__init__.py +0 -1
- eidosui-0.4.0/eidos/plugins/markdown/extensions/alerts.py +0 -134
- eidosui-0.4.0/eidos/tags.py +0 -194
- eidosui-0.4.0/eidos/utils.py +0 -72
- eidosui-0.4.0/pyproject.toml +0 -76
- {eidosui-0.4.0 → eidosui-0.5.0}/.gitignore +0 -0
- {eidosui-0.4.0 → eidosui-0.5.0}/LICENSE +0 -0
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/css/themes/dark.css +0 -0
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/css/themes/eidos-variables.css +0 -0
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/css/themes/light.css +0 -0
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/js/eidos.js +0 -0
- {eidosui-0.4.0 → eidosui-0.5.0}/eidos/plugins/markdown/css/markdown.css +0 -0
eidosui-0.5.0/PKG-INFO
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: eidosui
|
3
|
+
Version: 0.5.0
|
4
|
+
Summary: A modern, Tailwind CSS-based UI library for air development
|
5
|
+
Project-URL: Homepage, https://github.com/isaac-flath/EidosUI
|
6
|
+
Project-URL: Repository, https://github.com/isaac-flath/EidosUI
|
7
|
+
Project-URL: Issues, https://github.com/isaac-flath/EidosUI/issues
|
8
|
+
Project-URL: Documentation, https://github.com/isaac-flath/EidosUI#readme
|
9
|
+
Author: Isaac Flath
|
10
|
+
License-Expression: MIT
|
11
|
+
License-File: LICENSE
|
12
|
+
Keywords: air,components,css,fastapi,tailwind,ui,web
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
14
|
+
Classifier: Intended Audience :: Developers
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
23
|
+
Requires-Python: >=3.10
|
24
|
+
Requires-Dist: air>=0.12
|
25
|
+
Requires-Dist: fastapi[standard]
|
26
|
+
Requires-Dist: uvicorn
|
27
|
+
Provides-Extra: dev
|
28
|
+
Requires-Dist: black; extra == 'dev'
|
29
|
+
Requires-Dist: isort; extra == 'dev'
|
30
|
+
Requires-Dist: mypy; extra == 'dev'
|
31
|
+
Requires-Dist: pytest; extra == 'dev'
|
32
|
+
Requires-Dist: ruff; extra == 'dev'
|
33
|
+
Provides-Extra: markdown
|
34
|
+
Requires-Dist: markdown>=3.4; extra == 'markdown'
|
35
|
+
Description-Content-Type: text/markdown
|
36
|
+
|
37
|
+
# EidosUI
|
38
|
+
|
39
|
+
Modern UI library for Python web frameworks. Built on Air and Tailwind CSS.
|
40
|
+
|
41
|
+
> [!CAUTION]
|
42
|
+
> This library is in alpha, and may have semi-frequent breaking changes. I'd love for you to try it an contribute feedback or PRs!
|
43
|
+
|
44
|
+
## Installation
|
45
|
+
|
46
|
+
```bash
|
47
|
+
pip install eidosui
|
48
|
+
```
|
49
|
+
|
50
|
+
## Quick Start
|
51
|
+
|
52
|
+
```python
|
53
|
+
from eidos import *
|
54
|
+
import air
|
55
|
+
|
56
|
+
app = air.Air()
|
57
|
+
|
58
|
+
@app.get("/")
|
59
|
+
def home():
|
60
|
+
return Html(
|
61
|
+
Head(
|
62
|
+
Title("My App"),
|
63
|
+
*EidosHeaders() # Required CSS/JS
|
64
|
+
),
|
65
|
+
Body(
|
66
|
+
H1("Welcome"),
|
67
|
+
P("Build modern web apps with Python."),
|
68
|
+
DataTable.from_lists(
|
69
|
+
[["Alice", "30"], ["Bob", "25"]],
|
70
|
+
headers=["Name", "Age"]
|
71
|
+
)
|
72
|
+
)
|
73
|
+
)
|
74
|
+
|
75
|
+
app.run()
|
76
|
+
```
|
77
|
+
|
78
|
+
## Features
|
79
|
+
|
80
|
+
- **Styled HTML tags** - Pre-styled versions of all HTML elements
|
81
|
+
- **Components** - DataTable, NavBar, and more
|
82
|
+
- **Themes** - Light/dark themes via CSS variables
|
83
|
+
- **Type hints** - Full type annotations
|
84
|
+
- **Air integration** - Works seamlessly with Air framework
|
85
|
+
|
86
|
+
## Plugins
|
87
|
+
|
88
|
+
### Markdown
|
89
|
+
|
90
|
+
```bash
|
91
|
+
pip install "eidosui[markdown]"
|
92
|
+
```
|
93
|
+
|
94
|
+
```python
|
95
|
+
from eidos.plugins.markdown import Markdown, MarkdownCSS
|
96
|
+
|
97
|
+
Head(
|
98
|
+
*EidosHeaders(),
|
99
|
+
MarkdownCSS() # Add markdown styles
|
100
|
+
)
|
101
|
+
|
102
|
+
Body(
|
103
|
+
Markdown("# Hello\n\nSupports **GitHub Flavored Markdown**")
|
104
|
+
)
|
105
|
+
```
|
106
|
+
|
107
|
+
## Documentation
|
108
|
+
|
109
|
+
Full documentation: https://eidosui.readthedocs.io
|
110
|
+
|
111
|
+
## License
|
112
|
+
|
113
|
+
MIT
|
eidosui-0.5.0/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# EidosUI
|
2
|
+
|
3
|
+
Modern UI library for Python web frameworks. Built on Air and Tailwind CSS.
|
4
|
+
|
5
|
+
> [!CAUTION]
|
6
|
+
> This library is in alpha, and may have semi-frequent breaking changes. I'd love for you to try it an contribute feedback or PRs!
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
```bash
|
11
|
+
pip install eidosui
|
12
|
+
```
|
13
|
+
|
14
|
+
## Quick Start
|
15
|
+
|
16
|
+
```python
|
17
|
+
from eidos import *
|
18
|
+
import air
|
19
|
+
|
20
|
+
app = air.Air()
|
21
|
+
|
22
|
+
@app.get("/")
|
23
|
+
def home():
|
24
|
+
return Html(
|
25
|
+
Head(
|
26
|
+
Title("My App"),
|
27
|
+
*EidosHeaders() # Required CSS/JS
|
28
|
+
),
|
29
|
+
Body(
|
30
|
+
H1("Welcome"),
|
31
|
+
P("Build modern web apps with Python."),
|
32
|
+
DataTable.from_lists(
|
33
|
+
[["Alice", "30"], ["Bob", "25"]],
|
34
|
+
headers=["Name", "Age"]
|
35
|
+
)
|
36
|
+
)
|
37
|
+
)
|
38
|
+
|
39
|
+
app.run()
|
40
|
+
```
|
41
|
+
|
42
|
+
## Features
|
43
|
+
|
44
|
+
- **Styled HTML tags** - Pre-styled versions of all HTML elements
|
45
|
+
- **Components** - DataTable, NavBar, and more
|
46
|
+
- **Themes** - Light/dark themes via CSS variables
|
47
|
+
- **Type hints** - Full type annotations
|
48
|
+
- **Air integration** - Works seamlessly with Air framework
|
49
|
+
|
50
|
+
## Plugins
|
51
|
+
|
52
|
+
### Markdown
|
53
|
+
|
54
|
+
```bash
|
55
|
+
pip install "eidosui[markdown]"
|
56
|
+
```
|
57
|
+
|
58
|
+
```python
|
59
|
+
from eidos.plugins.markdown import Markdown, MarkdownCSS
|
60
|
+
|
61
|
+
Head(
|
62
|
+
*EidosHeaders(),
|
63
|
+
MarkdownCSS() # Add markdown styles
|
64
|
+
)
|
65
|
+
|
66
|
+
Body(
|
67
|
+
Markdown("# Hello\n\nSupports **GitHub Flavored Markdown**")
|
68
|
+
)
|
69
|
+
```
|
70
|
+
|
71
|
+
## Documentation
|
72
|
+
|
73
|
+
Full documentation: https://eidosui.readthedocs.io
|
74
|
+
|
75
|
+
## License
|
76
|
+
|
77
|
+
MIT
|
@@ -0,0 +1,270 @@
|
|
1
|
+
"""EidosUI - A modern, flexible Tailwind CSS-based UI library for Python web frameworks.
|
2
|
+
|
3
|
+
Quick start:
|
4
|
+
>>> from eidos import *
|
5
|
+
>>> DataTable.from_lists([["A", "B"], ["C", "D"]], headers=["Col1", "Col2"])
|
6
|
+
|
7
|
+
Or use explicit imports:
|
8
|
+
>>> from eidos import DataTable, Button, H1, Table
|
9
|
+
>>> from eidos import styles
|
10
|
+
"""
|
11
|
+
|
12
|
+
# Import all styled HTML tags
|
13
|
+
# Import style namespaces
|
14
|
+
from . import styles
|
15
|
+
|
16
|
+
# Import components
|
17
|
+
from .components import DataTable, EidosHeaders, NavBar
|
18
|
+
from .styles import buttons, semantic, tables, typography
|
19
|
+
from .tags import (
|
20
|
+
# Headings
|
21
|
+
H1,
|
22
|
+
H2,
|
23
|
+
H3,
|
24
|
+
H4,
|
25
|
+
H5,
|
26
|
+
H6,
|
27
|
+
# Pass-through HTML tags
|
28
|
+
A,
|
29
|
+
Abbr,
|
30
|
+
Address,
|
31
|
+
Area,
|
32
|
+
Article,
|
33
|
+
Aside,
|
34
|
+
Audio,
|
35
|
+
B,
|
36
|
+
Base,
|
37
|
+
Bdi,
|
38
|
+
Bdo,
|
39
|
+
Blockquote,
|
40
|
+
# Body
|
41
|
+
Body,
|
42
|
+
Br,
|
43
|
+
# Buttons
|
44
|
+
Button,
|
45
|
+
Canvas,
|
46
|
+
Caption,
|
47
|
+
Cite,
|
48
|
+
Code,
|
49
|
+
Col,
|
50
|
+
Colgroup,
|
51
|
+
Data,
|
52
|
+
Datalist,
|
53
|
+
Dd,
|
54
|
+
Del,
|
55
|
+
Details,
|
56
|
+
Dfn,
|
57
|
+
Dialog,
|
58
|
+
Div,
|
59
|
+
Dl,
|
60
|
+
Dt,
|
61
|
+
Em,
|
62
|
+
Embed,
|
63
|
+
Fieldset,
|
64
|
+
Figcaption,
|
65
|
+
Figure,
|
66
|
+
Footer,
|
67
|
+
Form,
|
68
|
+
Head,
|
69
|
+
Header,
|
70
|
+
Hgroup,
|
71
|
+
Hr,
|
72
|
+
Html,
|
73
|
+
I,
|
74
|
+
Iframe,
|
75
|
+
Img,
|
76
|
+
Input,
|
77
|
+
Ins,
|
78
|
+
Kbd,
|
79
|
+
Label,
|
80
|
+
Legend,
|
81
|
+
Li,
|
82
|
+
Link,
|
83
|
+
Main,
|
84
|
+
Map,
|
85
|
+
Mark,
|
86
|
+
Menu,
|
87
|
+
Meta,
|
88
|
+
Meter,
|
89
|
+
Nav,
|
90
|
+
Noscript,
|
91
|
+
Object,
|
92
|
+
Ol,
|
93
|
+
Optgroup,
|
94
|
+
Option,
|
95
|
+
Output,
|
96
|
+
P,
|
97
|
+
Param,
|
98
|
+
Picture,
|
99
|
+
Pre,
|
100
|
+
Progress,
|
101
|
+
Q,
|
102
|
+
Rp,
|
103
|
+
Rt,
|
104
|
+
Ruby,
|
105
|
+
S,
|
106
|
+
Samp,
|
107
|
+
Script,
|
108
|
+
Search,
|
109
|
+
Section,
|
110
|
+
Select,
|
111
|
+
Small,
|
112
|
+
Source,
|
113
|
+
Span,
|
114
|
+
# Semantic typography
|
115
|
+
Strong,
|
116
|
+
Style,
|
117
|
+
Sub,
|
118
|
+
Summary,
|
119
|
+
Sup,
|
120
|
+
# Table elements
|
121
|
+
Table,
|
122
|
+
Tbody,
|
123
|
+
Td,
|
124
|
+
Template,
|
125
|
+
Textarea,
|
126
|
+
Tfoot,
|
127
|
+
Th,
|
128
|
+
Thead,
|
129
|
+
Time,
|
130
|
+
Title,
|
131
|
+
Tr,
|
132
|
+
Track,
|
133
|
+
U,
|
134
|
+
Ul,
|
135
|
+
Var,
|
136
|
+
Video,
|
137
|
+
Wbr,
|
138
|
+
)
|
139
|
+
|
140
|
+
# Version info
|
141
|
+
__version__ = "0.4.0"
|
142
|
+
|
143
|
+
# Define what's available with "from eidos import *"
|
144
|
+
__all__ = [
|
145
|
+
# Version
|
146
|
+
"__version__",
|
147
|
+
# Style namespaces
|
148
|
+
"styles",
|
149
|
+
"buttons",
|
150
|
+
"typography",
|
151
|
+
"semantic",
|
152
|
+
"tables",
|
153
|
+
# Components
|
154
|
+
"DataTable",
|
155
|
+
"NavBar",
|
156
|
+
"EidosHeaders",
|
157
|
+
# HTML Tags
|
158
|
+
"H1",
|
159
|
+
"H2",
|
160
|
+
"H3",
|
161
|
+
"H4",
|
162
|
+
"H5",
|
163
|
+
"H6",
|
164
|
+
"Body",
|
165
|
+
"Button",
|
166
|
+
"Strong",
|
167
|
+
"I",
|
168
|
+
"Small",
|
169
|
+
"Del",
|
170
|
+
"Abbr",
|
171
|
+
"Var",
|
172
|
+
"Mark",
|
173
|
+
"Time",
|
174
|
+
"Code",
|
175
|
+
"Pre",
|
176
|
+
"Kbd",
|
177
|
+
"Samp",
|
178
|
+
"Blockquote",
|
179
|
+
"Cite",
|
180
|
+
"Address",
|
181
|
+
"Hr",
|
182
|
+
"Details",
|
183
|
+
"Summary",
|
184
|
+
"Dl",
|
185
|
+
"Dt",
|
186
|
+
"Dd",
|
187
|
+
"Figure",
|
188
|
+
"Figcaption",
|
189
|
+
"Table",
|
190
|
+
"Thead",
|
191
|
+
"Tbody",
|
192
|
+
"Tfoot",
|
193
|
+
"Tr",
|
194
|
+
"Th",
|
195
|
+
"Td",
|
196
|
+
"A",
|
197
|
+
"Area",
|
198
|
+
"Article",
|
199
|
+
"Aside",
|
200
|
+
"Audio",
|
201
|
+
"B",
|
202
|
+
"Base",
|
203
|
+
"Bdi",
|
204
|
+
"Bdo",
|
205
|
+
"Br",
|
206
|
+
"Canvas",
|
207
|
+
"Caption",
|
208
|
+
"Col",
|
209
|
+
"Colgroup",
|
210
|
+
"Data",
|
211
|
+
"Datalist",
|
212
|
+
"Dfn",
|
213
|
+
"Dialog",
|
214
|
+
"Div",
|
215
|
+
"Em",
|
216
|
+
"Embed",
|
217
|
+
"Fieldset",
|
218
|
+
"Footer",
|
219
|
+
"Form",
|
220
|
+
"Head",
|
221
|
+
"Header",
|
222
|
+
"Hgroup",
|
223
|
+
"Html",
|
224
|
+
"Iframe",
|
225
|
+
"Img",
|
226
|
+
"Input",
|
227
|
+
"Ins",
|
228
|
+
"Label",
|
229
|
+
"Legend",
|
230
|
+
"Li",
|
231
|
+
"Link",
|
232
|
+
"Main",
|
233
|
+
"Map",
|
234
|
+
"Menu",
|
235
|
+
"Meta",
|
236
|
+
"Meter",
|
237
|
+
"Nav",
|
238
|
+
"Noscript",
|
239
|
+
"Object",
|
240
|
+
"Ol",
|
241
|
+
"Optgroup",
|
242
|
+
"Option",
|
243
|
+
"Output",
|
244
|
+
"P",
|
245
|
+
"Param",
|
246
|
+
"Picture",
|
247
|
+
"Progress",
|
248
|
+
"Q",
|
249
|
+
"Rp",
|
250
|
+
"Rt",
|
251
|
+
"Ruby",
|
252
|
+
"S",
|
253
|
+
"Script",
|
254
|
+
"Search",
|
255
|
+
"Section",
|
256
|
+
"Select",
|
257
|
+
"Source",
|
258
|
+
"Span",
|
259
|
+
"Style",
|
260
|
+
"Sub",
|
261
|
+
"Sup",
|
262
|
+
"Template",
|
263
|
+
"Textarea",
|
264
|
+
"Title",
|
265
|
+
"Track",
|
266
|
+
"U",
|
267
|
+
"Ul",
|
268
|
+
"Video",
|
269
|
+
"Wbr",
|
270
|
+
]
|
@@ -1,16 +1,18 @@
|
|
1
|
-
from air import Meta, Script, Link
|
2
|
-
from ..tags import Body
|
3
1
|
from typing import Literal
|
4
2
|
|
3
|
+
from air import Link, Meta, Script
|
4
|
+
|
5
|
+
|
5
6
|
def get_css_urls():
|
6
7
|
"""Return list of CSS URLs for EidosUI."""
|
7
8
|
return [
|
8
9
|
"/eidos/css/styles.css",
|
9
|
-
"/eidos/css/themes/eidos-variables.css",
|
10
|
+
"/eidos/css/themes/eidos-variables.css",
|
10
11
|
"/eidos/css/themes/light.css",
|
11
|
-
"/eidos/css/themes/dark.css"
|
12
|
+
"/eidos/css/themes/dark.css",
|
12
13
|
]
|
13
14
|
|
15
|
+
|
14
16
|
def EidosHeaders(
|
15
17
|
include_tailwind: bool = True,
|
16
18
|
include_lucide: bool = True,
|
@@ -18,7 +20,7 @@ def EidosHeaders(
|
|
18
20
|
theme: Literal["light", "dark"] = "light",
|
19
21
|
):
|
20
22
|
"""Complete EidosUI headers with EidosUI JavaScript support.
|
21
|
-
|
23
|
+
|
22
24
|
Args:
|
23
25
|
include_tailwind: Include Tailwind CSS CDN
|
24
26
|
include_lucide: Include Lucide Icons CDN
|
@@ -29,28 +31,28 @@ def EidosHeaders(
|
|
29
31
|
Meta(charset="UTF-8"),
|
30
32
|
Meta(name="viewport", content="width=device-width, initial-scale=1.0"),
|
31
33
|
]
|
32
|
-
|
34
|
+
|
33
35
|
# Core libraries
|
34
36
|
if include_tailwind:
|
35
37
|
headers.append(Script(src="https://cdn.tailwindcss.com"))
|
36
|
-
|
38
|
+
|
37
39
|
if include_lucide:
|
38
40
|
headers.append(Script(src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"))
|
39
|
-
|
41
|
+
|
40
42
|
# EidosUI CSS
|
41
43
|
for css_url in get_css_urls():
|
42
44
|
headers.append(Link(rel="stylesheet", href=css_url))
|
43
|
-
|
45
|
+
|
44
46
|
# EidosUI JavaScript
|
45
47
|
if include_eidos_js:
|
46
48
|
headers.append(Script(src="/eidos/js/eidos.js", defer=True))
|
47
|
-
|
49
|
+
|
48
50
|
# Initialization script
|
49
51
|
init_script = f"""
|
50
52
|
// Set theme
|
51
53
|
document.documentElement.setAttribute('data-theme', '{theme}');
|
52
54
|
"""
|
53
|
-
|
55
|
+
|
54
56
|
if include_lucide:
|
55
57
|
init_script += """
|
56
58
|
// Initialize Lucide icons
|
@@ -62,7 +64,7 @@ def EidosHeaders(
|
|
62
64
|
if (window.lucide) lucide.createIcons();
|
63
65
|
}
|
64
66
|
"""
|
65
|
-
|
67
|
+
|
66
68
|
headers.append(Script(init_script))
|
67
|
-
|
68
|
-
return headers
|
69
|
+
|
70
|
+
return headers
|
@@ -0,0 +1,87 @@
|
|
1
|
+
from typing import Any, Final
|
2
|
+
from uuid import uuid4
|
3
|
+
|
4
|
+
from air import A, Div, I, Tag
|
5
|
+
|
6
|
+
from ..tags import *
|
7
|
+
from ..utils import stringify
|
8
|
+
|
9
|
+
|
10
|
+
class ScrollspyT:
|
11
|
+
underline: Final[str] = "navbar-underline"
|
12
|
+
bold: Final[str] = "navbar-bold"
|
13
|
+
|
14
|
+
|
15
|
+
def NavBar(
|
16
|
+
*c: Any,
|
17
|
+
lcontents: Tag | None = None,
|
18
|
+
right_cls: str = "items-center space-x-4",
|
19
|
+
mobile_cls: str = "",
|
20
|
+
sticky: bool = False,
|
21
|
+
scrollspy: bool = False,
|
22
|
+
cls: str = "p-4",
|
23
|
+
scrollspy_cls: str = ScrollspyT.underline,
|
24
|
+
menu_id: str | None = None,
|
25
|
+
) -> Tag:
|
26
|
+
"""Pure Tailwind responsive navigation bar with optional scrollspy.
|
27
|
+
|
28
|
+
Mobile menu uses best practice dropdown with:
|
29
|
+
- Centered text links
|
30
|
+
- Large touch targets
|
31
|
+
- Auto-close on selection
|
32
|
+
- Smooth animations
|
33
|
+
"""
|
34
|
+
if lcontents is None:
|
35
|
+
lcontents = Div()
|
36
|
+
|
37
|
+
if menu_id is None:
|
38
|
+
menu_id = f"menu-{uuid4().hex[:8]}"
|
39
|
+
|
40
|
+
sticky_cls = "sticky top-0 eidos-navbar-sticky z-50" if sticky else ""
|
41
|
+
|
42
|
+
# Mobile toggle button with hamburger/close icon
|
43
|
+
mobile_icon = A(
|
44
|
+
I(data_lucide="menu", class_="w-6 h-6", data_menu_icon="open"),
|
45
|
+
I(data_lucide="x", class_="w-6 h-6 hidden", data_menu_icon="close"),
|
46
|
+
class_="md:hidden cursor-pointer p-2 eidos-navbar-toggle rounded-lg transition-colors",
|
47
|
+
data_toggle=f"#{menu_id}",
|
48
|
+
role="button",
|
49
|
+
aria_label="Toggle navigation",
|
50
|
+
aria_expanded="false",
|
51
|
+
)
|
52
|
+
|
53
|
+
# Desktop navigation
|
54
|
+
desktop_nav = Div(
|
55
|
+
*c,
|
56
|
+
class_=stringify(right_cls, "hidden md:flex"),
|
57
|
+
data_scrollspy="true" if scrollspy else None,
|
58
|
+
)
|
59
|
+
|
60
|
+
# Mobile navigation
|
61
|
+
mobile_nav = Div(
|
62
|
+
*c,
|
63
|
+
class_=stringify(
|
64
|
+
mobile_cls,
|
65
|
+
"hidden md:hidden absolute top-full left-0 right-0 eidos-navbar-mobile shadow-lg border-t",
|
66
|
+
"flex flex-col eidos-navbar-mobile-divider" if not mobile_cls else "",
|
67
|
+
scrollspy_cls,
|
68
|
+
),
|
69
|
+
id=menu_id,
|
70
|
+
data_scrollspy="true" if scrollspy else None,
|
71
|
+
data_mobile_menu="true",
|
72
|
+
)
|
73
|
+
|
74
|
+
return Div(
|
75
|
+
# Main navbar container with relative positioning for mobile dropdown
|
76
|
+
Div(
|
77
|
+
Div(
|
78
|
+
lcontents,
|
79
|
+
mobile_icon,
|
80
|
+
desktop_nav,
|
81
|
+
class_="flex items-center justify-between",
|
82
|
+
),
|
83
|
+
mobile_nav,
|
84
|
+
class_=stringify("eidos-navbar relative", cls, scrollspy_cls),
|
85
|
+
),
|
86
|
+
class_=sticky_cls,
|
87
|
+
)
|
@@ -0,0 +1,84 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from ..tags import Table as BaseTable
|
4
|
+
from ..tags import Tbody, Td, Th, Thead, Tr
|
5
|
+
|
6
|
+
|
7
|
+
class DataTable:
|
8
|
+
"""DataTable component that provides convenient methods for creating tables from data."""
|
9
|
+
|
10
|
+
@classmethod
|
11
|
+
def from_lists(
|
12
|
+
cls,
|
13
|
+
data: list[list],
|
14
|
+
headers: list[str] | None = None,
|
15
|
+
class_: str | list[str] | None = None,
|
16
|
+
**kwargs: Any,
|
17
|
+
) -> Any:
|
18
|
+
"""Create table from list of lists.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
data: List of lists where each inner list is a row
|
22
|
+
headers: Optional list of header strings
|
23
|
+
class_: Optional CSS classes to add to the table
|
24
|
+
**kwargs: Additional attributes to pass to the table element
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
A rendered table element
|
28
|
+
|
29
|
+
Example:
|
30
|
+
Table.from_lists([["A", "B"], ["C", "D"]], headers=["Col1", "Col2"])
|
31
|
+
"""
|
32
|
+
content = []
|
33
|
+
|
34
|
+
if headers:
|
35
|
+
thead = Thead(Tr(*[Th(header) for header in headers]))
|
36
|
+
content.append(thead)
|
37
|
+
|
38
|
+
tbody_rows = []
|
39
|
+
for row_data in data:
|
40
|
+
tbody_rows.append(Tr(*[Td(cell) for cell in row_data]))
|
41
|
+
tbody = Tbody(*tbody_rows)
|
42
|
+
content.append(tbody)
|
43
|
+
|
44
|
+
return BaseTable(*content, class_=class_, **kwargs)
|
45
|
+
|
46
|
+
@classmethod
|
47
|
+
def from_dicts(
|
48
|
+
cls,
|
49
|
+
data: list[dict],
|
50
|
+
headers: list[str] | None = None,
|
51
|
+
class_: str | list[str] | None = None,
|
52
|
+
**kwargs: Any,
|
53
|
+
) -> Any:
|
54
|
+
"""Create table from list of dictionaries.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
data: List of dictionaries where each dict is a row
|
58
|
+
headers: Optional list of header strings. If not provided, uses keys from first dict
|
59
|
+
class_: Optional CSS classes to add to the table
|
60
|
+
**kwargs: Additional attributes to pass to the table element
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
A rendered table element
|
64
|
+
|
65
|
+
Example:
|
66
|
+
Table.from_dicts([{"name": "John", "age": 25}], headers=["name", "age"])
|
67
|
+
"""
|
68
|
+
if headers is None and data:
|
69
|
+
headers = list(data[0].keys())
|
70
|
+
|
71
|
+
content = []
|
72
|
+
|
73
|
+
if headers:
|
74
|
+
thead = Thead(Tr(*[Th(header) for header in headers]))
|
75
|
+
content.append(thead)
|
76
|
+
|
77
|
+
tbody_rows = []
|
78
|
+
if data and headers:
|
79
|
+
for row in data:
|
80
|
+
tbody_rows.append(Tr(*[Td(row.get(key, "")) for key in headers]))
|
81
|
+
tbody = Tbody(*tbody_rows)
|
82
|
+
content.append(tbody)
|
83
|
+
|
84
|
+
return BaseTable(*content, class_=class_, **kwargs)
|