eidosui 0.4.0__py3-none-any.whl → 0.5.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.
eidos/__init__.py CHANGED
@@ -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
+ ]
@@ -0,0 +1,10 @@
1
+ """EidosUI Components Package
2
+
3
+ Higher-level components built on top of the base tags.
4
+ """
5
+
6
+ from .headers import EidosHeaders
7
+ from .navigation import NavBar
8
+ from .table import DataTable
9
+
10
+ __all__ = ["DataTable", "NavBar", "EidosHeaders"]
@@ -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
@@ -1,35 +1,44 @@
1
- from air import Div, A, I, Tag
1
+ from typing import Any, Final
2
+ from uuid import uuid4
3
+
4
+ from air import A, Div, I, Tag
5
+
2
6
  from ..tags import *
3
7
  from ..utils import stringify
4
- from typing import Final, Optional, Any, Union
5
- from uuid import uuid4
8
+
6
9
 
7
10
  class ScrollspyT:
8
- underline: Final[str] = 'navbar-underline'
9
- bold: Final[str] = 'navbar-bold'
11
+ underline: Final[str] = "navbar-underline"
12
+ bold: Final[str] = "navbar-bold"
13
+
10
14
 
11
- def NavBar(*c: Any,
12
- lcontents: Tag = H3("Title"),
13
- right_cls: str = 'items-center space-x-4',
14
- mobile_cls: str = '',
15
- sticky: bool = False,
16
- scrollspy: bool = False,
17
- cls: str = 'p-4',
18
- scrollspy_cls: str = ScrollspyT.underline,
19
- menu_id: Optional[str] = None,
20
- ) -> Tag:
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:
21
26
  """Pure Tailwind responsive navigation bar with optional scrollspy.
22
-
27
+
23
28
  Mobile menu uses best practice dropdown with:
24
29
  - Centered text links
25
30
  - Large touch targets
26
31
  - Auto-close on selection
27
32
  - Smooth animations
28
33
  """
29
- if menu_id is None: menu_id = f"menu-{uuid4().hex[:8]}"
30
-
31
- sticky_cls = 'sticky top-0 eidos-navbar-sticky z-50' if sticky else ''
32
-
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
+
33
42
  # Mobile toggle button with hamburger/close icon
34
43
  mobile_icon = A(
35
44
  I(data_lucide="menu", class_="w-6 h-6", data_menu_icon="open"),
@@ -38,30 +47,30 @@ def NavBar(*c: Any,
38
47
  data_toggle=f"#{menu_id}",
39
48
  role="button",
40
49
  aria_label="Toggle navigation",
41
- aria_expanded="false"
50
+ aria_expanded="false",
42
51
  )
43
-
52
+
44
53
  # Desktop navigation
45
54
  desktop_nav = Div(
46
55
  *c,
47
- class_=stringify(right_cls, 'hidden md:flex'),
48
- data_scrollspy="true" if scrollspy else None
56
+ class_=stringify(right_cls, "hidden md:flex"),
57
+ data_scrollspy="true" if scrollspy else None,
49
58
  )
50
-
59
+
51
60
  # Mobile navigation
52
61
  mobile_nav = Div(
53
62
  *c,
54
63
  class_=stringify(
55
- mobile_cls,
56
- 'hidden md:hidden absolute top-full left-0 right-0 eidos-navbar-mobile shadow-lg border-t',
57
- 'flex flex-col eidos-navbar-mobile-divider' if not mobile_cls else '',
58
- scrollspy_cls
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,
59
68
  ),
60
69
  id=menu_id,
61
70
  data_scrollspy="true" if scrollspy else None,
62
- data_mobile_menu="true"
71
+ data_mobile_menu="true",
63
72
  )
64
-
73
+
65
74
  return Div(
66
75
  # Main navbar container with relative positioning for mobile dropdown
67
76
  Div(
@@ -69,10 +78,10 @@ def NavBar(*c: Any,
69
78
  lcontents,
70
79
  mobile_icon,
71
80
  desktop_nav,
72
- class_='flex items-center justify-between'
81
+ class_="flex items-center justify-between",
73
82
  ),
74
83
  mobile_nav,
75
- class_=stringify('eidos-navbar relative', cls, scrollspy_cls)
84
+ class_=stringify("eidos-navbar relative", cls, scrollspy_cls),
76
85
  ),
77
- class_=sticky_cls
78
- )
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)
eidos/css/styles.css CHANGED
@@ -339,6 +339,54 @@
339
339
  margin: var(--space-xl) 0;
340
340
  }
341
341
 
342
+ /* Table styles */
343
+ .eidos-table {
344
+ width: 100%;
345
+ border-collapse: collapse;
346
+ font-size: var(--font-size-base);
347
+ background-color: var(--color-background);
348
+ }
349
+
350
+ .eidos-thead {
351
+ background-color: var(--color-surface);
352
+ border-bottom: var(--border-2) solid var(--color-border);
353
+ }
354
+
355
+ .eidos-tbody {
356
+ background-color: var(--color-background);
357
+ }
358
+
359
+ .eidos-tfoot {
360
+ background-color: var(--color-surface);
361
+ border-top: var(--border-2) solid var(--color-border);
362
+ }
363
+
364
+ .eidos-tr {
365
+ border-bottom: var(--border) solid var(--color-border);
366
+ transition: background-color var(--transition-fast);
367
+ }
368
+
369
+ .eidos-tbody .eidos-tr:hover {
370
+ background-color: var(--color-surface);
371
+ }
372
+
373
+ .eidos-tbody .eidos-tr:last-child {
374
+ border-bottom: none;
375
+ }
376
+
377
+ .eidos-th {
378
+ padding: var(--space-sm) var(--space-md);
379
+ text-align: left;
380
+ font-weight: var(--font-weight-semibold);
381
+ color: var(--color-text);
382
+ }
383
+
384
+ .eidos-td {
385
+ padding: var(--space-sm) var(--space-md);
386
+ text-align: left;
387
+ color: var(--color-text);
388
+ }
389
+
342
390
  /* Navigation base styles */
343
391
  .eidos-navbar {
344
392
  width: 100%;
eidos/plugins/__init__.py CHANGED
@@ -1 +1 @@
1
- # EidosUI plugins package
1
+ # EidosUI plugins package
@@ -5,10 +5,10 @@ EidosUI themes through CSS variables.
5
5
 
6
6
  Basic usage:
7
7
  from eidos.plugins.markdown import Markdown, MarkdownCSS
8
-
8
+
9
9
  # In your document head
10
10
  MarkdownCSS()
11
-
11
+
12
12
  # In your content
13
13
  Markdown("# Hello World\\n\\nThis is **markdown**!")
14
14
  """
@@ -18,4 +18,4 @@ from .renderer import MarkdownRenderer
18
18
 
19
19
  __all__ = ["Markdown", "MarkdownCSS", "MarkdownRenderer"]
20
20
 
21
- __version__ = "0.1.0"
21
+ __version__ = "0.1.0"
@@ -1,53 +1,41 @@
1
1
  """Markdown components for EidosUI"""
2
2
 
3
3
  import air
4
- from typing import Optional
5
- from .renderer import MarkdownRenderer
6
4
 
5
+ from .renderer import MarkdownRenderer
7
6
 
8
7
  # Global renderer instance for reuse
9
8
  _renderer = MarkdownRenderer()
10
9
 
11
10
 
12
- def Markdown(content: str, class_: Optional[str] = None, **kwargs) -> air.Div:
11
+ def Markdown(content: str, class_: str | None = None, **kwargs) -> air.Div:
13
12
  """Main markdown component that renders markdown content with theme integration.
14
-
13
+
15
14
  Args:
16
15
  content: Markdown text to render
17
16
  class_: Additional CSS classes to apply
18
17
  **kwargs: Additional attributes to pass to the wrapper div
19
-
18
+
20
19
  Returns:
21
20
  air.Div containing the rendered markdown HTML
22
21
  """
23
22
  # Render the markdown content
24
23
  html_content = _renderer.render(content)
25
-
26
- # Create the div with raw HTML content
27
- if class_:
28
- return air.Div(
29
- air.RawHTML(html_content),
30
- class_=class_,
31
- **kwargs
32
- )
33
- else:
34
- return air.Div(
35
- air.RawHTML(html_content),
36
- **kwargs
37
- )
24
+
25
+ return air.Div(air.RawHTML(html_content), class_=class_, **kwargs)
38
26
 
39
27
 
40
28
  def MarkdownCSS() -> air.Link:
41
29
  """Returns a link tag to include the markdown CSS.
42
-
30
+
43
31
  This should be included in the head of your document to ensure
44
32
  markdown styling is available.
45
-
33
+
46
34
  Returns:
47
35
  air.Link element pointing to the markdown CSS file
48
36
  """
49
37
  return air.Link(
50
38
  rel="stylesheet",
51
39
  href="/eidos/plugins/markdown/css/markdown.css",
52
- type="text/css"
53
- )
40
+ type="text/css",
41
+ )