fastlifeweb 0.3.1__tar.gz → 0.4.1__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.
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/PKG-INFO +5 -4
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/README.md +0 -2
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/pyproject.toml +5 -2
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/configurator/registry.py +2 -2
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/configurator/settings.py +3 -0
- fastlifeweb-0.4.1/src/fastlife/templates/A.jinja +31 -0
- fastlifeweb-0.4.1/src/fastlife/templates/Button.jinja +41 -0
- fastlifeweb-0.4.1/src/fastlife/templates/Checkbox.jinja +20 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/CsrfToken.jinja +0 -1
- fastlifeweb-0.4.1/src/fastlife/templates/Form.jinja +11 -0
- fastlifeweb-0.4.1/src/fastlife/templates/H1.jinja +15 -0
- fastlifeweb-0.4.1/src/fastlife/templates/H2.jinja +15 -0
- fastlifeweb-0.4.1/src/fastlife/templates/H3.jinja +14 -0
- fastlifeweb-0.4.1/src/fastlife/templates/H4.jinja +14 -0
- fastlifeweb-0.4.1/src/fastlife/templates/H5.jinja +14 -0
- fastlifeweb-0.4.1/src/fastlife/templates/H6.jinja +14 -0
- fastlifeweb-0.4.1/src/fastlife/templates/Input.jinja +31 -0
- fastlifeweb-0.4.1/src/fastlife/templates/Label.jinja +11 -0
- fastlifeweb-0.4.1/src/fastlife/templates/Option.jinja +4 -0
- fastlifeweb-0.4.1/src/fastlife/templates/P.jinja +9 -0
- fastlifeweb-0.4.1/src/fastlife/templates/Radio.jinja +30 -0
- fastlifeweb-0.4.1/src/fastlife/templates/Select.jinja +28 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/pydantic_form/Boolean.jinja +1 -1
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/pydantic_form/Dropdown.jinja +4 -6
- fastlifeweb-0.4.1/src/fastlife/templates/pydantic_form/Hint.jinja +4 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/pydantic_form/Model.jinja +1 -1
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/pydantic_form/Sequence.jinja +3 -3
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/pydantic_form/Text.jinja +1 -3
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/pydantic_form/Union.jinja +5 -5
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/__init__.py +2 -2
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/binding.py +4 -22
- fastlifeweb-0.4.1/src/fastlife/templating/renderer/__init__.py +7 -0
- fastlifeweb-0.4.1/src/fastlife/templating/renderer/abstract.py +46 -0
- fastlifeweb-0.4.1/src/fastlife/templating/renderer/jinjax.py +111 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/renderer/widgets/base.py +12 -2
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/renderer/widgets/boolean.py +4 -3
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/renderer/widgets/dropdown.py +7 -6
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/renderer/widgets/factory.py +38 -21
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/renderer/widgets/hidden.py +2 -3
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/renderer/widgets/model.py +10 -7
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/renderer/widgets/sequence.py +8 -9
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/renderer/widgets/text.py +9 -5
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/renderer/widgets/union.py +7 -6
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/testing/testclient.py +116 -55
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/views/pydantic_form.py +5 -2
- fastlifeweb-0.3.1/src/fastlife/templates/A.jinja +0 -5
- fastlifeweb-0.3.1/src/fastlife/templates/Button.jinja +0 -27
- fastlifeweb-0.3.1/src/fastlife/templates/Checkbox.jinja +0 -2
- fastlifeweb-0.3.1/src/fastlife/templates/Form.jinja +0 -5
- fastlifeweb-0.3.1/src/fastlife/templates/H1.jinja +0 -4
- fastlifeweb-0.3.1/src/fastlife/templates/H2.jinja +0 -4
- fastlifeweb-0.3.1/src/fastlife/templates/Input.jinja +0 -8
- fastlifeweb-0.3.1/src/fastlife/templates/Label.jinja +0 -3
- fastlifeweb-0.3.1/src/fastlife/templates/Option.jinja +0 -2
- fastlifeweb-0.3.1/src/fastlife/templates/Radio.jinja +0 -8
- fastlifeweb-0.3.1/src/fastlife/templates/Select.jinja +0 -8
- fastlifeweb-0.3.1/src/fastlife/templating/renderer/__init__.py +0 -4
- fastlifeweb-0.3.1/src/fastlife/templating/renderer/abstract.py +0 -31
- fastlifeweb-0.3.1/src/fastlife/templating/renderer/jinjax.py +0 -73
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/LICENSE +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/__init__.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/configurator/__init__.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/configurator/base.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/configurator/configurator.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/py.typed +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/request/__init__.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/request/form_data.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/security/__init__.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/security/csrf.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/security/policy.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/session/__init__.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/session/middleware.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/session/serializer.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/shared_utils/__init__.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/shared_utils/infer.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/shared_utils/resolver.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/Hidden.jinja +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/__init__.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/pydantic_form/Hidden.jinja +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templates/pydantic_form/Widget.jinja +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/templating/renderer/widgets/__init__.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/testing/__init__.py +0 -0
- {fastlifeweb-0.3.1 → fastlifeweb-0.4.1}/src/fastlife/views/__init__.py +0 -0
@@ -1,7 +1,8 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fastlifeweb
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.1
|
4
4
|
Summary: High-level web framework
|
5
|
+
Home-page: https://github.com/mardiros/fastlife
|
5
6
|
License: BSD-derived
|
6
7
|
Author: Guillaume Gauvrit
|
7
8
|
Author-email: guillaume@gauvr.it
|
@@ -20,12 +21,14 @@ Requires-Dist: beautifulsoup4[testing] (>=4.12.2,<5.0.0)
|
|
20
21
|
Requires-Dist: behave (>=1.2.6,<2.0.0)
|
21
22
|
Requires-Dist: fastapi (>=0.110.0,<0.111.0)
|
22
23
|
Requires-Dist: itsdangerous (>=2.1.2,<3.0.0)
|
23
|
-
Requires-Dist: jinjax (>=0.
|
24
|
+
Requires-Dist: jinjax (>=0.32,<0.33)
|
24
25
|
Requires-Dist: markupsafe (>=2.1.3,<3.0.0)
|
26
|
+
Requires-Dist: multidict (>=6.0.5,<7.0.0)
|
25
27
|
Requires-Dist: pydantic (>=2.3.0,<3.0.0)
|
26
28
|
Requires-Dist: pydantic-settings (>=2.0.3,<3.0.0)
|
27
29
|
Requires-Dist: python-multipart (>=0.0.6,<0.0.7)
|
28
30
|
Requires-Dist: venusian (>=3.0.0,<4.0.0)
|
31
|
+
Project-URL: Repository, https://github.com/mardiros/fastlife
|
29
32
|
Description-Content-Type: text/markdown
|
30
33
|
|
31
34
|
# Fastlife
|
@@ -44,5 +47,3 @@ Under heavy development.
|
|
44
47
|
|
45
48
|
The package is available on pypi with the name fastlifeweb.
|
46
49
|
|
47
|
-
The name fastlife is squatted and I am tired of PEP 541 requests.
|
48
|
-
|
@@ -4,6 +4,8 @@ description = "High-level web framework"
|
|
4
4
|
name = "fastlifeweb"
|
5
5
|
license = "BSD-derived"
|
6
6
|
packages = [{include = "fastlife", from = "src"}]
|
7
|
+
homepage = "https://github.com/mardiros/fastlife"
|
8
|
+
repository = "https://github.com/mardiros/fastlife"
|
7
9
|
readme = "README.md"
|
8
10
|
classifiers = [
|
9
11
|
"Development Status :: 4 - Beta",
|
@@ -13,13 +15,13 @@ classifiers = [
|
|
13
15
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
14
16
|
"Topic :: Internet :: WWW/HTTP",
|
15
17
|
]
|
16
|
-
version = "0.
|
18
|
+
version = "0.4.1"
|
17
19
|
|
18
20
|
[tool.poetry.dependencies]
|
19
21
|
python = "^3.11"
|
20
22
|
beautifulsoup4 = {version = "^4.12.2", extras = ["testing"]}
|
21
23
|
fastapi = "^0.110.0"
|
22
|
-
jinjax = "^0.
|
24
|
+
jinjax = "^0.32"
|
23
25
|
pydantic = "^2.3.0"
|
24
26
|
pydantic-settings = "^2.0.3"
|
25
27
|
python-multipart = "^0.0.6"
|
@@ -27,6 +29,7 @@ venusian = "^3.0.0"
|
|
27
29
|
markupsafe = "^2.1.3"
|
28
30
|
behave = "^1.2.6"
|
29
31
|
itsdangerous = "^2.1.2"
|
32
|
+
multidict = "^6.0.5"
|
30
33
|
|
31
34
|
[tool.poetry.group.dev.dependencies]
|
32
35
|
beautifulsoup4 = "^4.12.2"
|
@@ -7,7 +7,7 @@ from fastlife.shared_utils.resolver import resolve
|
|
7
7
|
|
8
8
|
if TYPE_CHECKING:
|
9
9
|
from fastlife.templating.renderer import ( # coverage: ignore
|
10
|
-
|
10
|
+
AbstractTemplateRendererFactory, # coverage: ignore
|
11
11
|
) # coverage: ignore
|
12
12
|
|
13
13
|
from .settings import Settings
|
@@ -15,7 +15,7 @@ from .settings import Settings
|
|
15
15
|
|
16
16
|
class AppRegistry:
|
17
17
|
settings: Settings
|
18
|
-
renderer: "
|
18
|
+
renderer: "AbstractTemplateRendererFactory"
|
19
19
|
check_permission: CheckPermission
|
20
20
|
|
21
21
|
def __init__(self, settings: Settings) -> None:
|
@@ -17,6 +17,9 @@ class Settings(BaseSettings):
|
|
17
17
|
form_data_model_prefix: str = Field(default="payload")
|
18
18
|
csrf_token_name: str = Field(default="csrf_token")
|
19
19
|
|
20
|
+
jinjax_use_cache: bool = Field(default=True)
|
21
|
+
jinjax_auto_reload: bool = Field(default=False)
|
22
|
+
|
20
23
|
session_secret_key: str = Field(default="")
|
21
24
|
session_cookie_name: str = Field(default="flsess")
|
22
25
|
session_cookie_domain: str = Field(default="")
|
@@ -0,0 +1,31 @@
|
|
1
|
+
{# def
|
2
|
+
href,
|
3
|
+
disable_htmx=false,
|
4
|
+
hx_target="#maincontent",
|
5
|
+
hx_select=None,
|
6
|
+
hx_swap="innerHTML show:body:top",
|
7
|
+
hx_push_url=true,
|
8
|
+
class_="""
|
9
|
+
bg-neutral-200
|
10
|
+
px-5
|
11
|
+
py-2.5
|
12
|
+
rounded-lg
|
13
|
+
text-center
|
14
|
+
text-neutral-900
|
15
|
+
text-sm
|
16
|
+
font-medium
|
17
|
+
focus:outline-none
|
18
|
+
focus:ring-4
|
19
|
+
focus:ring-primary-300
|
20
|
+
hover:bg-neutral-50
|
21
|
+
dark:bg-neutral-400
|
22
|
+
dark:focus:ring-neutral-100
|
23
|
+
dark:hover:bg-neutral-300
|
24
|
+
"""
|
25
|
+
#}
|
26
|
+
|
27
|
+
<a href="{{href}}" {% if not disable_htmx %} hx-get="{{href}}" hx-target="{{hx_target}}" hx-swap="{{hx_swap}}" {% if
|
28
|
+
hx_push_url %} hx-push-url="true" {%- endif %}{% if hx_select %} hx-select="{{hx_select}}" {%- endif %}{%- endif
|
29
|
+
%}class="{{attrs.class or class_}}">
|
30
|
+
{{- content -}}
|
31
|
+
</a>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
{# def
|
2
|
+
type="submit",
|
3
|
+
id="",
|
4
|
+
name="action",
|
5
|
+
value="submit",
|
6
|
+
hidden=false,
|
7
|
+
aria_label="",
|
8
|
+
onclick="",
|
9
|
+
hx_target="",
|
10
|
+
hx_swap="",
|
11
|
+
hx_get="",
|
12
|
+
hx_select="",
|
13
|
+
hx_after_request="",
|
14
|
+
hx_vals="",
|
15
|
+
full_width=false,
|
16
|
+
class_="""
|
17
|
+
bg-primary-600
|
18
|
+
font-medium
|
19
|
+
px-5
|
20
|
+
py-2.5
|
21
|
+
rounded-lg
|
22
|
+
text-center
|
23
|
+
text-sm
|
24
|
+
text-white
|
25
|
+
hover:bg-primary-700
|
26
|
+
focus:outline-none
|
27
|
+
focus:ring-4
|
28
|
+
focus:ring-primary-300
|
29
|
+
dark:bg-primary-600
|
30
|
+
dark:focus:ring-primary-800
|
31
|
+
dark:hover:bg-primary-700
|
32
|
+
"""
|
33
|
+
#}
|
34
|
+
<button type="{{type}}" {%if id %}id="{{id}}" {%endif%} name="{{name}}" value="{{value}}" {% if
|
35
|
+
hx_target%}hx-target="{{hx_target}}" {% endif %} {% if hx_swap %}hx-swap="{{hx_swap}}" {% endif %} {% if hx_select
|
36
|
+
%}hx-select="{{hx_select}}" {% endif %} {% if hx_get %}hx-get="{{hx_get}}" {% endif %} {% if onclick %}onclick="{{onclick}}" {%
|
37
|
+
endif %}{% if hx_after_request %}hx-on::after-request="{{hx_after_request}}" {% endif %} {% if hx_vals
|
38
|
+
%}hx-vals='{{hx_vals|safe}}' {% endif %} {% if aria_label %}aria-label="{{aria_label}}" {% endif %}
|
39
|
+
class="{% if full_width %}w-full {% endif %}{{attrs.class or class_}}" {% if hidden %}hidden{% endif %}>
|
40
|
+
{{- content -}}
|
41
|
+
</button>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{# def
|
2
|
+
name,
|
3
|
+
value,
|
4
|
+
id,
|
5
|
+
class_="""
|
6
|
+
bg-neutral-100
|
7
|
+
border-neutral-300
|
8
|
+
h-4
|
9
|
+
rounded
|
10
|
+
text-primary-600
|
11
|
+
w-4
|
12
|
+
dark:bg-neutral-700
|
13
|
+
dark:border-neutral-600
|
14
|
+
dark:focus:ring-primary-600
|
15
|
+
dark:ring-offset-neutral-800
|
16
|
+
focus:ring-2
|
17
|
+
focus:ring-primary-500
|
18
|
+
"""
|
19
|
+
#}
|
20
|
+
<input name="{{name}}" {% if value%}checked{% endif %} type="checkbox" id="{{id}}" class="{{attrs.class or class_}}" />
|
@@ -0,0 +1,11 @@
|
|
1
|
+
{# def
|
2
|
+
method="",
|
3
|
+
action=None,
|
4
|
+
hx_post=None,
|
5
|
+
class_="space-y-4 md:space-y-6"
|
6
|
+
#}
|
7
|
+
<form class="{{attrs.class or class_}}" {% if hx_post is not none %}hx-post="{% if hx_post is not true %}{{hx_post}}{% endif%}" {% endif %}{% if action is not
|
8
|
+
none %}action="{{action}}" {% endif %}{% if method %}method="{{method}}" {% endif %}>
|
9
|
+
<CsrfToken />
|
10
|
+
{{ content }}
|
11
|
+
</form>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
{# def
|
2
|
+
name,
|
3
|
+
value="",
|
4
|
+
type="text",
|
5
|
+
id="",
|
6
|
+
aria_label="",
|
7
|
+
placeholder="",
|
8
|
+
class_="""
|
9
|
+
bg-neutral-50
|
10
|
+
block
|
11
|
+
border
|
12
|
+
border-neutral-300
|
13
|
+
p-2.5
|
14
|
+
rounded-lg
|
15
|
+
text-base
|
16
|
+
text-neutral-900
|
17
|
+
w-full
|
18
|
+
dark:bg-neutral-700
|
19
|
+
dark:border-neutral-600
|
20
|
+
dark:focus:border-primary-500
|
21
|
+
dark:focus:ring-primary-500
|
22
|
+
dark:placeholder-neutral-400
|
23
|
+
dark:text-white
|
24
|
+
focus:border-primary-500
|
25
|
+
focus:ring-primary-500
|
26
|
+
"""
|
27
|
+
#}
|
28
|
+
|
29
|
+
<input name="{{name}}" value="{{value}}" type="{{type}}" {% if id %}id="{{id}}" {% endif %}{% if aria_label
|
30
|
+
%}aria-label="{{aria_label}}" {% endif %} {% if placeholder %}placeholder="{{placeholder}}" {% endif %}
|
31
|
+
class="{{attrs.class or class_}}" />
|
@@ -0,0 +1,30 @@
|
|
1
|
+
{# def
|
2
|
+
label,
|
3
|
+
name,
|
4
|
+
id,
|
5
|
+
value,
|
6
|
+
checked=false,
|
7
|
+
disabled=false,
|
8
|
+
onclick="",
|
9
|
+
class_="""
|
10
|
+
bg-neutral-100
|
11
|
+
border-neutral-300
|
12
|
+
w-4
|
13
|
+
h-4
|
14
|
+
text-primary-600
|
15
|
+
focus:ring-2
|
16
|
+
focus:ring-primary-500
|
17
|
+
dark:bg-neutral-700
|
18
|
+
dark:border-neutral-600
|
19
|
+
dark:focus:ring-primary-600
|
20
|
+
dark:ring-offset-neutral-800
|
21
|
+
""",
|
22
|
+
label_class="ms-2 text-sm font-medium text-neutral-900 dark:text-neutral-300",
|
23
|
+
div_class="flex items-center mb-4"
|
24
|
+
#}
|
25
|
+
<div class="{{div_class}}">
|
26
|
+
<input type="radio" name="{{name}}" id="{{id}}" value="{{value}}" {% if checked %}checked{% endif %}
|
27
|
+
class="{{attrs.class or class_}}" {% if onclick %}onclick="{{onclick}}" {% endif %} {%if
|
28
|
+
disabled%}disabled{%endif%}>
|
29
|
+
<Label for={id} class={label_class}>{{label}}</Label>
|
30
|
+
</div>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
{# def
|
2
|
+
name,
|
3
|
+
id,
|
4
|
+
multiple=False,
|
5
|
+
class_="""
|
6
|
+
bg-neutral-50
|
7
|
+
block
|
8
|
+
border
|
9
|
+
border-neutral-300
|
10
|
+
p-2.5
|
11
|
+
rounded-lg
|
12
|
+
text-base
|
13
|
+
text-neutral-900
|
14
|
+
w-full
|
15
|
+
focus:border-primary-500
|
16
|
+
focus:ring-primary-500
|
17
|
+
dark:bg-neutral-700
|
18
|
+
dark:border-neutral-600
|
19
|
+
dark:focus:border-primary-500
|
20
|
+
dark:focus:ring-primary-500
|
21
|
+
dark:placeholder-neutral-400
|
22
|
+
dark:text-white
|
23
|
+
"""
|
24
|
+
#}
|
25
|
+
|
26
|
+
<select name="{{name}}" id="{{id}}" class="{{attrs.class or class_}}" {% if multiple %}multiple{% endif %}>
|
27
|
+
{{- content -}}
|
28
|
+
</select>
|
@@ -2,6 +2,6 @@
|
|
2
2
|
<pydantic_form.Widget widget={widget} removable={widget.removable}>
|
3
3
|
<div class="pt-4">
|
4
4
|
<Label for={widget.id}>{{widget.title}}</Label>
|
5
|
-
<Checkbox name={widget.name}
|
5
|
+
<Checkbox name={widget.name} type="checkbox" id={widget.id} {% if widget.value %}checked{% endif %} />
|
6
6
|
</div>
|
7
7
|
</pydantic_form.Widget>
|
@@ -5,13 +5,11 @@
|
|
5
5
|
<Label for={widget.id}>{{widget.title}}</Label>
|
6
6
|
<Select name={widget.name} id={widget.id}>
|
7
7
|
{%- for opt in widget.options -%}
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
<Option value={opt.value} id={widget.id + "-" + opt.value} selected={widget.value==opt.value}>
|
9
|
+
{{ opt.text }}
|
10
|
+
</Option>
|
11
11
|
{%- endfor -%}
|
12
12
|
</Select>
|
13
|
-
{
|
14
|
-
<span class="mt-2 text-sm text-neutral-500 dark:text-neutral-400">{{widget.help_text}}</span>
|
15
|
-
{%- endif %}
|
13
|
+
<pydantic_form.Hint text={widget.hint} />
|
16
14
|
</div>
|
17
15
|
</pydantic_form.Widget>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<div id="{{widget.id}}" class="m-4">
|
5
5
|
<details open>
|
6
6
|
<summary class="justify-between items-center font-medium cursor-pointer">
|
7
|
-
<
|
7
|
+
<H3>{{widget.title}}</H3>
|
8
8
|
</summary>
|
9
9
|
<div>
|
10
10
|
{% for child in children_widget %}
|
@@ -3,12 +3,12 @@
|
|
3
3
|
<pydantic_form.Widget widget={widget} removable={widget.removable}>
|
4
4
|
<details id="{{widget.id}}" open>
|
5
5
|
<summary class="justify-between items-center font-medium cursor-pointer">
|
6
|
-
<
|
6
|
+
<H3>{{widget.title}}</H3>
|
7
7
|
</summary>
|
8
8
|
<div>
|
9
9
|
{% set fnGetName = "get" + widget.id.replace("-", "_") %}
|
10
10
|
<script>
|
11
|
-
function {{fnGetName}}() {
|
11
|
+
function {{ fnGetName }} () {
|
12
12
|
const el = document.getElementById("{{widget.id}}-content");
|
13
13
|
const len = el.dataset.length;
|
14
14
|
el.dataset.length = parseInt(len) + 1;
|
@@ -28,7 +28,7 @@
|
|
28
28
|
{% set container_id = "#" + widget.id + "-container" %}
|
29
29
|
{% set add_id = widget.id + "-add" %}
|
30
30
|
{% set vals = 'js:{"name": '+ fnGetName + '(), "token": "' + type.token + '", "removable": true}' %}
|
31
|
-
<Button type="button" target={container_id} swap="beforeend" id={add_id} vals={vals} get={type.url}>
|
31
|
+
<Button type="button" hx-target={container_id} hx-swap="beforeend" id={add_id} hx-vals={vals} hx-get={type.url}>
|
32
32
|
Add
|
33
33
|
</Button>
|
34
34
|
</div>
|
@@ -5,8 +5,6 @@
|
|
5
5
|
<Label for={widget.id}>{{widget.title}}</Label>
|
6
6
|
<Input name={widget.name} value={widget.value} type={widget.input_type} id={widget.id}
|
7
7
|
aria-label={widget.aria_label} placeholder={widget.placeholder} />
|
8
|
-
{
|
9
|
-
<span class="mt-2 text-sm text-neutral-500 dark:text-neutral-400">{{widget.help_text}}</span>
|
10
|
-
{%- endif %}
|
8
|
+
<pydantic_form.Hint text={widget.hint} />
|
11
9
|
</div>
|
12
10
|
</pydantic_form.Widget>
|
@@ -4,21 +4,21 @@
|
|
4
4
|
<div id="{{widget.id}}">
|
5
5
|
<details open>
|
6
6
|
<summary class="justify-between items-center font-medium cursor-pointer">
|
7
|
-
<
|
7
|
+
<H3>{{widget.title}}</H3>
|
8
8
|
</summary>
|
9
9
|
<div hx-sync="this" id="{{widget.id}}-child">
|
10
10
|
{% if child %}
|
11
11
|
{{ child }}
|
12
12
|
{% else %}
|
13
13
|
{% for typ in types %}
|
14
|
-
<Button type="button" target="closest div" get={typ.url} vals={typ.params|tojson}
|
15
|
-
id={typ.
|
14
|
+
<Button type="button" hx-target="closest div" hx-get={typ.url} hx-vals={typ.params|tojson}
|
15
|
+
id={typ.id}
|
16
16
|
onclick={"document.getElementById('" + widget.id +"-remove-btn').hidden=false"}>{{typ.title}}</Button>
|
17
17
|
{% endfor %}
|
18
18
|
{% endif %}
|
19
19
|
</div>
|
20
|
-
<Button type="button" id={widget.id + "-remove-btn" } target={"#" + widget.id} vals={parent_type.params|tojson}
|
21
|
-
get={parent_type.url} hidden={not child}>
|
20
|
+
<Button type="button" id={widget.id + "-remove-btn" } hx-target={"#" + widget.id} hx-vals={parent_type.params|tojson}
|
21
|
+
hx-get={parent_type.url} hidden={not child}>
|
22
22
|
Remove
|
23
23
|
</Button>
|
24
24
|
</details>
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from .binding import Template, template
|
2
|
-
from .renderer import
|
2
|
+
from .renderer import AbstractTemplateRendererFactory, JinjaxTemplateRenderer
|
3
3
|
|
4
4
|
__all__ = [
|
5
|
-
"
|
5
|
+
"AbstractTemplateRendererFactory",
|
6
6
|
"JinjaxTemplateRenderer",
|
7
7
|
"Template",
|
8
8
|
"template",
|
@@ -9,9 +9,7 @@ Template = Callable[..., Response]
|
|
9
9
|
TemplateEngine = Callable[["Registry", Request], Template]
|
10
10
|
|
11
11
|
|
12
|
-
def
|
13
|
-
template: str, *, content_type: str = "text/html"
|
14
|
-
) -> TemplateEngine:
|
12
|
+
def get_template(template: str, *, content_type: str = "text/html") -> TemplateEngine:
|
15
13
|
def render_template(
|
16
14
|
reg: "Registry",
|
17
15
|
request: Request,
|
@@ -23,7 +21,7 @@ def get_page_template(
|
|
23
21
|
request.cookies.get(reg.settings.csrf_token_name)
|
24
22
|
or _create_csrf_token()
|
25
23
|
)
|
26
|
-
data = reg.renderer.
|
24
|
+
data = reg.renderer(request).render_template(template, **kwargs)
|
27
25
|
resp = Response(data, headers={"Content-Type": content_type})
|
28
26
|
resp.set_cookie(
|
29
27
|
reg.settings.csrf_token_name,
|
@@ -39,21 +37,5 @@ def get_page_template(
|
|
39
37
|
return render_template
|
40
38
|
|
41
39
|
|
42
|
-
def
|
43
|
-
|
44
|
-
) -> Callable[["Registry"], Template]:
|
45
|
-
def render_template(reg: "Registry") -> Template:
|
46
|
-
def parametrizer(**kwargs: Any) -> Response:
|
47
|
-
data = reg.renderer.render_template(template, **kwargs)
|
48
|
-
resp = Response(data, headers={"Content-Type": content_type})
|
49
|
-
return resp
|
50
|
-
|
51
|
-
return parametrizer
|
52
|
-
|
53
|
-
return render_template
|
54
|
-
|
55
|
-
|
56
|
-
def template(template_path: str, page: bool = True) -> Template:
|
57
|
-
return Depends(
|
58
|
-
get_page_template(template_path) if page else get_template(template_path)
|
59
|
-
)
|
40
|
+
def template(template_path: str) -> Template:
|
41
|
+
return Depends(get_template(template_path))
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import abc
|
2
|
+
from typing import Any, Mapping, Optional, Type
|
3
|
+
|
4
|
+
from fastapi import Request
|
5
|
+
from markupsafe import Markup
|
6
|
+
from pydantic.fields import FieldInfo
|
7
|
+
|
8
|
+
|
9
|
+
class AbstractTemplateRenderer(abc.ABC):
|
10
|
+
route_prefix: str
|
11
|
+
"""Used to buid pydantic form"""
|
12
|
+
|
13
|
+
@abc.abstractmethod
|
14
|
+
def render_template(
|
15
|
+
self,
|
16
|
+
template: str,
|
17
|
+
*,
|
18
|
+
globals: Optional[Mapping[str, Any]] = None,
|
19
|
+
**params: Any,
|
20
|
+
) -> str:
|
21
|
+
"""
|
22
|
+
Render the given template with the given params.
|
23
|
+
|
24
|
+
While rendering templates, the globals parameter is keps by the instantiated
|
25
|
+
renderer and sent to every rendering made by the request.
|
26
|
+
This is used by the pydantic form method that will render other templates
|
27
|
+
for the request.
|
28
|
+
"""
|
29
|
+
|
30
|
+
@abc.abstractmethod
|
31
|
+
def pydantic_form(
|
32
|
+
self,
|
33
|
+
model: Type[Any],
|
34
|
+
form_data: Mapping[str, Any] | None = None,
|
35
|
+
name: str | None = None,
|
36
|
+
token: str | None = None,
|
37
|
+
removable: bool = False,
|
38
|
+
field: FieldInfo | None = None,
|
39
|
+
) -> Markup:
|
40
|
+
...
|
41
|
+
|
42
|
+
|
43
|
+
class AbstractTemplateRendererFactory(abc.ABC):
|
44
|
+
@abc.abstractmethod
|
45
|
+
def __call__(self, request: Request) -> AbstractTemplateRenderer:
|
46
|
+
...
|