pydzn 0.1.3__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.
- pydzn-0.1.4/PKG-INFO +168 -0
- pydzn-0.1.4/README.md +132 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/pyproject.toml +2 -2
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/components/__init__.py +3 -1
- pydzn-0.1.4/src/pydzn/components/button/component.py +88 -0
- pydzn-0.1.4/src/pydzn/components/card/component.py +93 -0
- pydzn-0.1.4/src/pydzn/components/hamburger_menu/component.py +210 -0
- pydzn-0.1.4/src/pydzn/components/nav_item/component.py +220 -0
- pydzn-0.1.4/src/pydzn/components/sidebar/component.py +70 -0
- pydzn-0.1.4/src/pydzn/components/sidebar/template.html +3 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/dzn.py +96 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/grid_builder.py +77 -18
- pydzn-0.1.4/src/pydzn/responsive.py +24 -0
- pydzn-0.1.4/src/pydzn/variants.py +215 -0
- pydzn-0.1.4/src/pydzn.egg-info/PKG-INFO +168 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn.egg-info/SOURCES.txt +4 -0
- pydzn-0.1.3/PKG-INFO +0 -38
- pydzn-0.1.3/README.md +0 -2
- pydzn-0.1.3/src/pydzn/components/button/component.py +0 -28
- pydzn-0.1.3/src/pydzn/components/card/component.py +0 -15
- pydzn-0.1.3/src/pydzn/components/nav_item/component.py +0 -56
- pydzn-0.1.3/src/pydzn/components/sidebar/component.py +0 -15
- pydzn-0.1.3/src/pydzn.egg-info/PKG-INFO +0 -38
- {pydzn-0.1.3 → pydzn-0.1.4}/LICENSE +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/setup.cfg +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/__init__.py +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/base_component.py +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/components/button/__init__.py +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/components/button/template.html +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/components/card/template.html +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/components/drawer/component.py +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/components/drawer/template.html +0 -0
- {pydzn-0.1.3/src/pydzn/components/nav_item → pydzn-0.1.4/src/pydzn/components/hamburger_menu}/template.html +0 -0
- {pydzn-0.1.3/src/pydzn/components/sidebar → pydzn-0.1.4/src/pydzn/components/nav_item}/template.html +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/components/text/component.py +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn/components/text/template.html +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn.egg-info/dependency_links.txt +0 -0
- {pydzn-0.1.3 → pydzn-0.1.4}/src/pydzn.egg-info/requires.txt +0 -0
- {pydzn-0.1.3 → 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.
|
8
|
-
description = "
|
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" }
|
@@ -1,8 +1,10 @@
|
|
1
1
|
from .button.component import Button
|
2
|
+
from .card.component import Card
|
2
3
|
from .text.component import Text
|
3
4
|
from .drawer.component import Drawer
|
4
5
|
from .sidebar.component import Sidebar
|
5
6
|
from .nav_item.component import NavItem
|
7
|
+
from .hamburger_menu.component import HamburgerMenu
|
6
8
|
|
7
9
|
|
8
|
-
__all__ = ["Button", "Text", "Drawer", "Sidebar", "NavItem"]
|
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 {}
|