pydzn 0.1.2__tar.gz → 0.1.4__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.
Files changed (38) hide show
  1. pydzn-0.1.4/PKG-INFO +168 -0
  2. pydzn-0.1.4/README.md +132 -0
  3. {pydzn-0.1.2 → pydzn-0.1.4}/pyproject.toml +2 -2
  4. {pydzn-0.1.2 → pydzn-0.1.4}/src/pydzn/base_component.py +0 -1
  5. pydzn-0.1.4/src/pydzn/components/__init__.py +10 -0
  6. pydzn-0.1.4/src/pydzn/components/button/component.py +88 -0
  7. pydzn-0.1.4/src/pydzn/components/card/component.py +93 -0
  8. pydzn-0.1.4/src/pydzn/components/card/template.html +3 -0
  9. pydzn-0.1.4/src/pydzn/components/drawer/component.py +14 -0
  10. pydzn-0.1.4/src/pydzn/components/drawer/template.html +3 -0
  11. pydzn-0.1.4/src/pydzn/components/hamburger_menu/component.py +210 -0
  12. pydzn-0.1.4/src/pydzn/components/hamburger_menu/template.html +3 -0
  13. pydzn-0.1.4/src/pydzn/components/nav_item/component.py +220 -0
  14. pydzn-0.1.4/src/pydzn/components/nav_item/template.html +3 -0
  15. pydzn-0.1.4/src/pydzn/components/sidebar/component.py +70 -0
  16. pydzn-0.1.4/src/pydzn/components/sidebar/template.html +3 -0
  17. pydzn-0.1.4/src/pydzn/components/text/component.py +15 -0
  18. pydzn-0.1.4/src/pydzn/components/text/template.html +3 -0
  19. {pydzn-0.1.2 → pydzn-0.1.4}/src/pydzn/dzn.py +96 -0
  20. {pydzn-0.1.2 → pydzn-0.1.4}/src/pydzn/grid_builder.py +77 -18
  21. pydzn-0.1.4/src/pydzn/responsive.py +24 -0
  22. pydzn-0.1.4/src/pydzn/variants.py +215 -0
  23. pydzn-0.1.4/src/pydzn.egg-info/PKG-INFO +168 -0
  24. pydzn-0.1.4/src/pydzn.egg-info/SOURCES.txt +30 -0
  25. pydzn-0.1.2/PKG-INFO +0 -38
  26. pydzn-0.1.2/README.md +0 -2
  27. pydzn-0.1.2/src/pydzn/components/button/__init__.py +0 -0
  28. pydzn-0.1.2/src/pydzn/components/button/component.py +0 -28
  29. pydzn-0.1.2/src/pydzn.egg-info/PKG-INFO +0 -38
  30. pydzn-0.1.2/src/pydzn.egg-info/SOURCES.txt +0 -16
  31. {pydzn-0.1.2 → pydzn-0.1.4}/LICENSE +0 -0
  32. {pydzn-0.1.2 → pydzn-0.1.4}/setup.cfg +0 -0
  33. {pydzn-0.1.2 → pydzn-0.1.4}/src/pydzn/__init__.py +0 -0
  34. {pydzn-0.1.2/src/pydzn/components → pydzn-0.1.4/src/pydzn/components/button}/__init__.py +0 -0
  35. {pydzn-0.1.2 → pydzn-0.1.4}/src/pydzn/components/button/template.html +0 -0
  36. {pydzn-0.1.2 → pydzn-0.1.4}/src/pydzn.egg-info/dependency_links.txt +0 -0
  37. {pydzn-0.1.2 → pydzn-0.1.4}/src/pydzn.egg-info/requires.txt +0 -0
  38. {pydzn-0.1.2 → pydzn-0.1.4}/src/pydzn.egg-info/top_level.txt +0 -0
pydzn-0.1.4/PKG-INFO ADDED
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.4
2
+ Name: pydzn
3
+ Version: 0.1.4
4
+ Summary: Build complex websites in pure python with pydzn layout builder, semantic css classes and an extendable components library for server-side rendering.
5
+ Author-email: Ryan Kirkish <ryan@foo.com>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/anthonyrka/pydzn
8
+ Project-URL: Repository, https://github.com/anthonyrka/pydzn
9
+ Project-URL: Issues, https://github.com/anthonyrka/pydzn/issues
10
+ Keywords: design-system,css,utility-classes,jinja2,components,grid,htmx
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Framework :: Flask
20
+ Classifier: Topic :: Internet :: WWW/HTTP
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: Jinja2>=3.1
26
+ Provides-Extra: flask
27
+ Requires-Dist: Flask>=2.3; extra == "flask"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8; extra == "dev"
30
+ Requires-Dist: ruff>=0.5; extra == "dev"
31
+ Requires-Dist: black>=24; extra == "dev"
32
+ Requires-Dist: mypy>=1.10; extra == "dev"
33
+ Requires-Dist: build>=1.2; extra == "dev"
34
+ Requires-Dist: twine>=5; extra == "dev"
35
+ Dynamic: license-file
36
+
37
+ # pydzn
38
+ *Pronounced:* **[paɪ dɪˈzaɪn]** — “**pie design**” (aka **py-design**)
39
+
40
+ Build full websites in Python with server-side components, a design system that leverages semantic classes, and a grid layout builder.
41
+
42
+ ## What is pydzn
43
+ pydzn or "py design" is a lightweight python library that makes it easy to design, build and serve complex websites all in python. It provides an api into CSS-grid for designing layouts and serves as a light-weight website builder with a built-in, and extendable, component library as well as a library for setting CSS semantic classes.
44
+
45
+
46
+ ## Examples
47
+ - For examples see: [pydzn-website](https://github.com/anthonyrka/pydzn-website)
48
+
49
+ ## References
50
+ - See [PyPI](https://pypi.org/project/pydzn/)
51
+
52
+
53
+ ## A website builder for python developers (not front-end developers)
54
+ The layout builder contains a debug mode allowing you to visualize the structure of your layout. Each named region is a slot which can be passed a sub-layout or a component in the layout's render function.
55
+
56
+ <p align="center">
57
+ <img src="docs/website_builder_sortof.gif" alt="mobile" width="640">
58
+ </p>
59
+
60
+ ## Responsive is built-in
61
+ Build responsive layouts with pydzn
62
+
63
+ <p align="center">
64
+ <img src="docs/pydzn_responsive.gif" alt="mobile" width="640">
65
+ </p>
66
+
67
+ The above layouts are combined desktop and mobile versions:
68
+ ```python
69
+
70
+ from pydzn.grid_builder import layout_builder
71
+
72
+ # This is the Main App layout structure we've created below for DESKTOP
73
+ """
74
+ column::left_column column::main_column
75
+ row:header_row region:left_sidebar region:appbar
76
+ row:content_row region:left_sidebar region:content
77
+
78
+ The layout accepts the following components in render function signature: left_sidebar, appbar, content
79
+ """
80
+ AppMainLayout = (
81
+ layout_builder()
82
+ .fill_height("100vh", property="height") # sets up the page to restrict height to view height
83
+ .columns(left_column=LEFT_SIDEBAR_WIDTH, main_column="1fr") # split the main layout into two columns: sidebar and main
84
+ .rows(header_row=HEADER_HEIGHT, content_row="1fr") # add 2 rows: a header section and a content section stacked
85
+ .region("left_sidebar", col="left_column", row="header_row", row_span=2) # place the sidebar into the left_sidebar column in the first row and span both rows
86
+ .region("appbar", col="main_column", row="header_row", col_span=1) # Add the appbar to the right of sidebar in the main section and span the last row
87
+ .region("content", col="main_column", row="content_row") # declare the empty content section which is the last row in main
88
+ .build(name="AppMainLayout")
89
+ )
90
+
91
+ # This is the Main App layout structure we've created below for MOBILE
92
+ """
93
+ column::main_column
94
+ row:header_row region:appbar
95
+ row:content_row region:content
96
+
97
+ The layout accepts the following components in render function signature: appbar, content
98
+ """
99
+ AppMainMobileLayout = (
100
+ layout_builder()
101
+ .fill_height("100vh", property="height")
102
+ .columns(main_column="1fr")
103
+ .rows(header_row=HEADER_HEIGHT_MOBILE, content_row="1fr")
104
+ .region("appbar", col="main_column", row="header_row")
105
+ .region("content", col="main_column", row="content_row")
106
+ .build(name="AppMainMobileLayout")
107
+ )
108
+
109
+ # ----- App header menu slots -----#
110
+ AppHeaderMenuLayout = (
111
+ layout_builder()
112
+ # make the inner grid fill the parent (the appbar row), not the viewport
113
+ .fill_height("100%", property="height") # was min-height:100vh (implicit default)
114
+ .columns(brand=BRAND_WIDTH, spacer="1fr", tasks=APP_MENU_WIDTH, customers=APP_MENU_WIDTH, orders=APP_MENU_WIDTH, notifications=APP_MENU_WIDTH, user_profile=APP_MENU_WIDTH)
115
+ .rows(app_header_main=f"minmax({HEADER_HEIGHT}px, auto)") # these items don't take up the full height of the app header unless you set it here
116
+ .region("brand", col="brand", row="app_header_main")
117
+ .region("spacer", col="spacer", row="app_header_main") # empty; pushes the rest right
118
+ .region("tasks", col="tasks", row="app_header_main")
119
+ .region("customers", col="customers", row="app_header_main")
120
+ .region("orders", col="orders", row="app_header_main")
121
+ .region("notifications", col="notifications", row="app_header_main")
122
+ .region("user_profile", col="user_profile", row="app_header_main")
123
+ .build(name="AppHeaderMenuLayout")
124
+ )
125
+
126
+ # ----- App header mobile menu layout ----#
127
+ AppHeaderMobileMenuLayout = (
128
+ layout_builder()
129
+ .fill_height("100%", property="height")
130
+ .columns(brand_col=BRAND_WIDTH, spacer_col="1fr", hamburger_menu_col=100)
131
+ .rows(app_header_mobile_row=f"minmax({HEADER_HEIGHT_MOBILE}px, auto)") # this container holds the app header for mobile layout so height must match
132
+ .region("brand", col="brand_col", row="app_header_mobile_row")
133
+ .region("hamburger_menu", col="hamburger_menu_col", row="app_header_mobile_row")
134
+ .build(name="AppHeaderMobileMenuLayout")
135
+ )
136
+
137
+ ```
138
+
139
+ These layouts contain render functions with a signature containing the named slots for variables, in the case of AppMainMobileLayout, the hamburger_menu slot is passed a built-in hamburger menu component:
140
+
141
+ ```python
142
+ from pydzn.components import NavItem, Text, HamburgerMenu
143
+
144
+ # Right-side full-height drawer
145
+ menu_btn = HamburgerMenu(
146
+ mode="right",
147
+ drawer_width=320,
148
+ show_backdrop=True,
149
+ children=drop_down_mobile,
150
+ dzn="bg-[white]", # forwarded to the panel automatically
151
+ panel_dzn="p-[24px]" # this is how you set the semantic css classes for the drawer (panel)
152
+ ).render()
153
+
154
+
155
+ mobile_html = AppHeaderMobileMenuLayout(
156
+ debug=debug,
157
+ region_dzn = {
158
+ "brand": "flex justify-center items-center",
159
+ "hamburger_menu": "flex justify-center items-center"
160
+ }
161
+ ).render(
162
+ brand=brand,
163
+ hamburger_menu=menu_btn
164
+ )
165
+ ```
166
+
167
+
168
+
pydzn-0.1.4/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # pydzn
2
+ *Pronounced:* **[paɪ dɪˈzaɪn]** — “**pie design**” (aka **py-design**)
3
+
4
+ Build full websites in Python with server-side components, a design system that leverages semantic classes, and a grid layout builder.
5
+
6
+ ## What is pydzn
7
+ pydzn or "py design" is a lightweight python library that makes it easy to design, build and serve complex websites all in python. It provides an api into CSS-grid for designing layouts and serves as a light-weight website builder with a built-in, and extendable, component library as well as a library for setting CSS semantic classes.
8
+
9
+
10
+ ## Examples
11
+ - For examples see: [pydzn-website](https://github.com/anthonyrka/pydzn-website)
12
+
13
+ ## References
14
+ - See [PyPI](https://pypi.org/project/pydzn/)
15
+
16
+
17
+ ## A website builder for python developers (not front-end developers)
18
+ The layout builder contains a debug mode allowing you to visualize the structure of your layout. Each named region is a slot which can be passed a sub-layout or a component in the layout's render function.
19
+
20
+ <p align="center">
21
+ <img src="docs/website_builder_sortof.gif" alt="mobile" width="640">
22
+ </p>
23
+
24
+ ## Responsive is built-in
25
+ Build responsive layouts with pydzn
26
+
27
+ <p align="center">
28
+ <img src="docs/pydzn_responsive.gif" alt="mobile" width="640">
29
+ </p>
30
+
31
+ The above layouts are combined desktop and mobile versions:
32
+ ```python
33
+
34
+ from pydzn.grid_builder import layout_builder
35
+
36
+ # This is the Main App layout structure we've created below for DESKTOP
37
+ """
38
+ column::left_column column::main_column
39
+ row:header_row region:left_sidebar region:appbar
40
+ row:content_row region:left_sidebar region:content
41
+
42
+ The layout accepts the following components in render function signature: left_sidebar, appbar, content
43
+ """
44
+ AppMainLayout = (
45
+ layout_builder()
46
+ .fill_height("100vh", property="height") # sets up the page to restrict height to view height
47
+ .columns(left_column=LEFT_SIDEBAR_WIDTH, main_column="1fr") # split the main layout into two columns: sidebar and main
48
+ .rows(header_row=HEADER_HEIGHT, content_row="1fr") # add 2 rows: a header section and a content section stacked
49
+ .region("left_sidebar", col="left_column", row="header_row", row_span=2) # place the sidebar into the left_sidebar column in the first row and span both rows
50
+ .region("appbar", col="main_column", row="header_row", col_span=1) # Add the appbar to the right of sidebar in the main section and span the last row
51
+ .region("content", col="main_column", row="content_row") # declare the empty content section which is the last row in main
52
+ .build(name="AppMainLayout")
53
+ )
54
+
55
+ # This is the Main App layout structure we've created below for MOBILE
56
+ """
57
+ column::main_column
58
+ row:header_row region:appbar
59
+ row:content_row region:content
60
+
61
+ The layout accepts the following components in render function signature: appbar, content
62
+ """
63
+ AppMainMobileLayout = (
64
+ layout_builder()
65
+ .fill_height("100vh", property="height")
66
+ .columns(main_column="1fr")
67
+ .rows(header_row=HEADER_HEIGHT_MOBILE, content_row="1fr")
68
+ .region("appbar", col="main_column", row="header_row")
69
+ .region("content", col="main_column", row="content_row")
70
+ .build(name="AppMainMobileLayout")
71
+ )
72
+
73
+ # ----- App header menu slots -----#
74
+ AppHeaderMenuLayout = (
75
+ layout_builder()
76
+ # make the inner grid fill the parent (the appbar row), not the viewport
77
+ .fill_height("100%", property="height") # was min-height:100vh (implicit default)
78
+ .columns(brand=BRAND_WIDTH, spacer="1fr", tasks=APP_MENU_WIDTH, customers=APP_MENU_WIDTH, orders=APP_MENU_WIDTH, notifications=APP_MENU_WIDTH, user_profile=APP_MENU_WIDTH)
79
+ .rows(app_header_main=f"minmax({HEADER_HEIGHT}px, auto)") # these items don't take up the full height of the app header unless you set it here
80
+ .region("brand", col="brand", row="app_header_main")
81
+ .region("spacer", col="spacer", row="app_header_main") # empty; pushes the rest right
82
+ .region("tasks", col="tasks", row="app_header_main")
83
+ .region("customers", col="customers", row="app_header_main")
84
+ .region("orders", col="orders", row="app_header_main")
85
+ .region("notifications", col="notifications", row="app_header_main")
86
+ .region("user_profile", col="user_profile", row="app_header_main")
87
+ .build(name="AppHeaderMenuLayout")
88
+ )
89
+
90
+ # ----- App header mobile menu layout ----#
91
+ AppHeaderMobileMenuLayout = (
92
+ layout_builder()
93
+ .fill_height("100%", property="height")
94
+ .columns(brand_col=BRAND_WIDTH, spacer_col="1fr", hamburger_menu_col=100)
95
+ .rows(app_header_mobile_row=f"minmax({HEADER_HEIGHT_MOBILE}px, auto)") # this container holds the app header for mobile layout so height must match
96
+ .region("brand", col="brand_col", row="app_header_mobile_row")
97
+ .region("hamburger_menu", col="hamburger_menu_col", row="app_header_mobile_row")
98
+ .build(name="AppHeaderMobileMenuLayout")
99
+ )
100
+
101
+ ```
102
+
103
+ These layouts contain render functions with a signature containing the named slots for variables, in the case of AppMainMobileLayout, the hamburger_menu slot is passed a built-in hamburger menu component:
104
+
105
+ ```python
106
+ from pydzn.components import NavItem, Text, HamburgerMenu
107
+
108
+ # Right-side full-height drawer
109
+ menu_btn = HamburgerMenu(
110
+ mode="right",
111
+ drawer_width=320,
112
+ show_backdrop=True,
113
+ children=drop_down_mobile,
114
+ dzn="bg-[white]", # forwarded to the panel automatically
115
+ panel_dzn="p-[24px]" # this is how you set the semantic css classes for the drawer (panel)
116
+ ).render()
117
+
118
+
119
+ mobile_html = AppHeaderMobileMenuLayout(
120
+ debug=debug,
121
+ region_dzn = {
122
+ "brand": "flex justify-center items-center",
123
+ "hamburger_menu": "flex justify-center items-center"
124
+ }
125
+ ).render(
126
+ brand=brand,
127
+ hamburger_menu=menu_btn
128
+ )
129
+ ```
130
+
131
+
132
+
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pydzn"
7
- version = "0.1.2"
8
- description = "Tiny design-system utilities (Tailwind-like), composable HTML components, and a grid layout builder for Python/Jinja apps."
7
+ version = "0.1.4"
8
+ description = "Build complex websites in pure python with pydzn layout builder, semantic css classes and an extendable components library for server-side rendering."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = { text = "Apache-2.0" }
@@ -6,7 +6,6 @@ import json
6
6
  import inspect
7
7
  from pathlib import Path
8
8
  from jinja2 import Environment, FileSystemLoader, select_autoescape
9
- from .base_agent import BaseAssistant
10
9
  from .dzn import register_dzn_classes
11
10
 
12
11
 
@@ -0,0 +1,10 @@
1
+ from .button.component import Button
2
+ from .card.component import Card
3
+ from .text.component import Text
4
+ from .drawer.component import Drawer
5
+ from .sidebar.component import Sidebar
6
+ from .nav_item.component import NavItem
7
+ from .hamburger_menu.component import HamburgerMenu
8
+
9
+
10
+ __all__ = ["Button", "Card", "Text", "Drawer", "Sidebar", "NavItem", "HamburgerMenu"]
@@ -0,0 +1,88 @@
1
+ from pydzn.base_component import BaseComponent
2
+ from pydzn.variants import VariantSupport
3
+
4
+
5
+ class Button(VariantSupport, BaseComponent):
6
+ """
7
+ Server-rendered Button with pluggable variants/sizes.
8
+
9
+ - Built-in variants keep colors inline so it works without a theme pack.
10
+ - You can add external libraries and select with namespace, e.g. variant="acme:glass".
11
+ """
12
+
13
+ # visual “structures” (kept color-aware so no theme pack needed)
14
+ VARIANTS = {
15
+ # solid
16
+ "solid-primary": (
17
+ "rounded-sm border border-transparent "
18
+ "bg-[#2563eb] text-[white] "
19
+ "shadow-md hover:shadow-lg"
20
+ ),
21
+ # outline
22
+ "outline-primary": (
23
+ "rounded-sm border-2 border-blue-500 "
24
+ "bg-[transparent] text-[#2563eb] "
25
+ "shadow-sm hover:shadow-md"
26
+ ),
27
+ # ghost (neutral)
28
+ "ghost-neutral": (
29
+ "rounded-sm border-0 "
30
+ "bg-[transparent] text-body "
31
+ "shadow-none hover:bg-[rgba(15,23,42,.06)]"
32
+ ),
33
+ # linky button
34
+ "link-primary": (
35
+ "rounded-none border-0 "
36
+ "bg-[transparent] text-[#2563eb] "
37
+ "shadow-none hover:underline"
38
+ ),
39
+ }
40
+
41
+ # density
42
+ SIZES = {
43
+ "sm": "px-3 py-1",
44
+ "md": "px-5 py-2",
45
+ "lg": "px-6 py-3",
46
+ "xl": "px-8 py-4",
47
+ }
48
+
49
+ # tones are optional here; variants carry color already
50
+ TONES = {}
51
+
52
+ # project-wide defaults can be overridden via VariantSupport.set_default_choices(Button, ...)
53
+ DEFAULTS = {
54
+ "variant": "outline-primary",
55
+ "size": "md",
56
+ "tone": "",
57
+ }
58
+
59
+ def __init__(
60
+ self,
61
+ text: str = "",
62
+ children: str | None = None,
63
+ *,
64
+ tag: str = "button",
65
+ # variant system
66
+ variant: str | None = None,
67
+ size: str | None = None,
68
+ tone: str | None = None,
69
+ # raw utility escape hatch (merged last)
70
+ dzn: str | None = None,
71
+ **attrs,
72
+ ):
73
+ # allow stray attrs["dzn"] too
74
+ extra_dzn = dzn or attrs.pop("dzn", None)
75
+
76
+ # resolve VARIANT + SIZE (+ optional TONE) + extra_dzn
77
+ effective_dzn = self._resolve_variant_dzn(
78
+ variant=variant,
79
+ size=size,
80
+ tone=tone,
81
+ extra_dzn=extra_dzn,
82
+ )
83
+
84
+ super().__init__(children=children, tag=tag, dzn=effective_dzn, **attrs)
85
+ self.text = text
86
+
87
+ def context(self) -> dict:
88
+ return {"text": self.text}
@@ -0,0 +1,93 @@
1
+ from pydzn.base_component import BaseComponent
2
+ from pydzn.variants import VariantSupport
3
+
4
+
5
+ class Card(VariantSupport, BaseComponent):
6
+ """
7
+ Server-rendered Card with pluggable variants/sizes/tones.
8
+ Variants focus on layout/structure; tones mainly adjust border color.
9
+ """
10
+
11
+ # visual “structures”
12
+ VARIANTS = {
13
+ "panel": (
14
+ "flex flex-col gap-4 p-4 rounded-xl "
15
+ "border border-subtle bg-elevated shadow-sm"
16
+ ),
17
+ "plain": (
18
+ "flex flex-col gap-3 p-4 rounded-md "
19
+ "border border-subtle bg-surface shadow-none"
20
+ ),
21
+ "elevated": (
22
+ "flex flex-col gap-4 p-4 rounded-xl "
23
+ "border border-transparent bg-elevated shadow-lg"
24
+ ),
25
+ "outlined": (
26
+ "flex flex-col gap-4 p-4 rounded-lg "
27
+ "border-2 border-slate-300 bg-[transparent] shadow-none"
28
+ ),
29
+ "soft": (
30
+ "flex flex-col gap-4 p-4 rounded-xl "
31
+ "border-0 bg-[rgba(15,23,42,.03)] shadow-sm"
32
+ ),
33
+ "glass": (
34
+ "flex flex-col gap-4 p-4 rounded-xl "
35
+ "border border-[rgba(255,255,255,.25)] bg-[rgba(255,255,255,.08)] shadow-md"
36
+ ),
37
+ "ghost": (
38
+ "flex flex-col gap-4 p-4 rounded-md "
39
+ "border-0 bg-[transparent] shadow-none"
40
+ ),
41
+ }
42
+
43
+ # density
44
+ SIZES = {
45
+ "xs": "p-2 gap-2 rounded-sm",
46
+ "sm": "p-3 gap-2 rounded-md",
47
+ "md": "p-4 gap-3 rounded-lg",
48
+ "lg": "p-6 gap-4 rounded-xl",
49
+ "xl": "p-8 gap-5 rounded-2xl",
50
+ }
51
+
52
+ # tones mainly drive border color (kept subtle by default)
53
+ TONES = {
54
+ "neutral": "border-subtle",
55
+ "primary": "border-blue-500",
56
+ "success": "border-green-500",
57
+ "danger": "border-red-500",
58
+ }
59
+
60
+ # project-wide defaults can be overridden at runtime:
61
+ # Card.set_default_choices(variant="...", size="...", tone="...")
62
+ DEFAULTS = {
63
+ "variant": "panel",
64
+ "size": "md",
65
+ "tone": "neutral",
66
+ }
67
+
68
+ def __init__(
69
+ self,
70
+ children: str | None = None,
71
+ *,
72
+ tag: str = "div",
73
+ # variant system
74
+ variant: str | None = None,
75
+ size: str | None = None,
76
+ tone: str | None = None,
77
+ # raw utility escape hatch (merged last)
78
+ dzn: str | None = None,
79
+ **attrs,
80
+ ):
81
+ extra_dzn = dzn or attrs.pop("dzn", None)
82
+
83
+ effective_dzn = self._resolve_variant_dzn(
84
+ variant=variant,
85
+ size=size,
86
+ tone=tone,
87
+ extra_dzn=extra_dzn,
88
+ )
89
+
90
+ super().__init__(children=children, tag=tag, dzn=effective_dzn, **attrs)
91
+
92
+ def context(self) -> dict:
93
+ return {}
@@ -0,0 +1,3 @@
1
+ <{{ tag }}{% if attrs %} {{ attrs|safe }}{% endif %}>
2
+ {{ children|safe }}
3
+ </{{ tag }}>
@@ -0,0 +1,14 @@
1
+ from pydzn.base_component import BaseComponent
2
+
3
+
4
+ class Drawer(BaseComponent):
5
+ """
6
+ Renders a drawer element.
7
+ Expects `template.html`
8
+ """
9
+
10
+ def __init__(self, children: str | None = None, tag: str = "div", **html_attrs):
11
+ super().__init__(children=children, tag=tag, **html_attrs)
12
+
13
+ def context(self) -> dict:
14
+ return {}
@@ -0,0 +1,3 @@
1
+ <{{ tag }}{% if attrs %} {{ attrs|safe }}{% endif %}>
2
+ {{ children|safe }}
3
+ </{{ tag }}>