accrete 0.0.54__py3-none-any.whl → 0.0.56__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.
- accrete/contrib/ui/__init__.py +2 -1
- accrete/contrib/ui/context.py +50 -8
- accrete/contrib/ui/elements.py +9 -9
- accrete/contrib/ui/filter.py +5 -5
- accrete/contrib/ui/static/css/accrete.css +17 -6
- accrete/contrib/ui/static/css/accrete.css.map +1 -1
- accrete/contrib/ui/static/css/accrete.scss +25 -0
- accrete/contrib/ui/templates/ui/dashboard.html +7 -0
- accrete/contrib/ui/templates/ui/detail.html +0 -86
- accrete/contrib/ui/templates/ui/form.html +6 -15
- accrete/contrib/ui/templates/ui/layout.html +3 -1
- accrete/contrib/ui/templates/ui/partials/filter.html +2 -2
- accrete/contrib/ui/templates/ui/partials/form_errors.html +33 -20
- accrete/contrib/ui/templates/ui/partials/header.html +1 -1
- accrete/forms.py +1 -248
- accrete/managers.py +1 -2
- accrete/utils/__init__.py +2 -2
- accrete/utils/forms.py +79 -0
- accrete/utils/http.py +14 -3
- {accrete-0.0.54.dist-info → accrete-0.0.56.dist-info}/METADATA +1 -1
- {accrete-0.0.54.dist-info → accrete-0.0.56.dist-info}/RECORD +23 -22
- {accrete-0.0.54.dist-info → accrete-0.0.56.dist-info}/WHEEL +1 -1
- {accrete-0.0.54.dist-info → accrete-0.0.56.dist-info}/licenses/LICENSE +0 -0
accrete/contrib/ui/__init__.py
CHANGED
accrete/contrib/ui/context.py
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
import logging
|
2
|
+
import json
|
2
3
|
from urllib.parse import quote_plus
|
3
4
|
from dataclasses import dataclass, field
|
4
|
-
from typing import TypedDict, Callable
|
5
|
+
from typing import Type, TypedDict, Callable
|
5
6
|
from django.utils.translation import gettext_lazy as _t
|
6
7
|
from django.db import models
|
7
8
|
from django.db.models import Model, QuerySet
|
8
9
|
from django.core.paginator import Paginator
|
9
|
-
from django.core import paginator
|
10
|
+
from django.core import paginator, exceptions
|
10
11
|
from django.forms import Form, ModelForm
|
11
12
|
from accrete.annotation import Annotation
|
12
13
|
from .elements import (
|
@@ -14,7 +15,7 @@ from .elements import (
|
|
14
15
|
ClientActionGroup
|
15
16
|
)
|
16
17
|
from .filter import Filter
|
17
|
-
|
18
|
+
from accrete.utils import filter_from_querystring
|
18
19
|
|
19
20
|
_logger = logging.getLogger(__name__)
|
20
21
|
|
@@ -60,6 +61,46 @@ class TableContext(Context):
|
|
60
61
|
field_selection: bool = True
|
61
62
|
|
62
63
|
|
64
|
+
def default_table_context(model: Type[Model], params: dict, queryset=None, **kwargs) -> TableContext:
|
65
|
+
if queryset is None:
|
66
|
+
queryset = filter_from_querystring(model, params)
|
67
|
+
page = list_page(
|
68
|
+
queryset,
|
69
|
+
cast_param(params, 'paginate_by', int, 40),
|
70
|
+
cast_param(params, 'page', int, 1)
|
71
|
+
)
|
72
|
+
try:
|
73
|
+
has_name_field = (
|
74
|
+
model._meta.get_field('name').get_internal_type() == 'CharField'
|
75
|
+
)
|
76
|
+
except exceptions.FieldDoesNotExist:
|
77
|
+
has_name_field = False
|
78
|
+
|
79
|
+
ctx = TableContext(
|
80
|
+
title=str(model._meta.verbose_name_plural),
|
81
|
+
object_label=str(model._meta.verbose_name),
|
82
|
+
object_param_str=url_param_str(
|
83
|
+
params,
|
84
|
+
['q', 'paginate_by', 'page', 'fields']
|
85
|
+
),
|
86
|
+
fields=get_table_fields(
|
87
|
+
cast_param(params, 'fields', json.loads, []), model
|
88
|
+
),
|
89
|
+
list_page=page,
|
90
|
+
pagination_param_str=url_param_str(
|
91
|
+
params, ['q', 'paginate_by', 'fields']
|
92
|
+
),
|
93
|
+
endless_scroll=True,
|
94
|
+
filter=Filter(
|
95
|
+
model,
|
96
|
+
default_filter_term='name__icontains' if has_name_field else ''
|
97
|
+
),
|
98
|
+
kwargs=kwargs
|
99
|
+
)
|
100
|
+
return ctx
|
101
|
+
|
102
|
+
|
103
|
+
|
63
104
|
@dataclass
|
64
105
|
class ListContext(Context):
|
65
106
|
|
@@ -67,7 +108,7 @@ class ListContext(Context):
|
|
67
108
|
object_param_str: str
|
68
109
|
list_page: paginator.Page
|
69
110
|
pagination_param_str: str
|
70
|
-
filter: Filter
|
111
|
+
filter: Filter = None
|
71
112
|
endless_scroll: bool = True
|
72
113
|
column_height: int = 4
|
73
114
|
column_width: int = 12
|
@@ -93,7 +134,9 @@ class DetailContext(Context):
|
|
93
134
|
class FormContext(Context):
|
94
135
|
|
95
136
|
form: Form | ModelForm
|
96
|
-
form_id: str
|
137
|
+
form_id: str = 'form'
|
138
|
+
form_method: str = 'post'
|
139
|
+
form_action: str = ''
|
97
140
|
|
98
141
|
|
99
142
|
def cast_param(params: dict, param: str, cast_to: Callable, default):
|
@@ -115,9 +158,8 @@ def url_param_dict(get_params: dict) -> dict:
|
|
115
158
|
|
116
159
|
def url_param_str(params: dict, extract: list[str] = None) -> str:
|
117
160
|
"""
|
118
|
-
Return a URL Querystring from the given
|
119
|
-
If extract is supplied,
|
120
|
-
them, so that each value is formatted e.g. {'page': '&page=1'}
|
161
|
+
Return a URL Querystring from the given params dict.
|
162
|
+
If extract is supplied, only specified keys will be used.
|
121
163
|
"""
|
122
164
|
if extract:
|
123
165
|
params = extract_url_params(params, extract)
|
accrete/contrib/ui/elements.py
CHANGED
@@ -25,15 +25,6 @@ class ActionMethod(Enum):
|
|
25
25
|
DELETE = 'hx-delete'
|
26
26
|
|
27
27
|
|
28
|
-
@dataclass
|
29
|
-
class ClientActionGroup:
|
30
|
-
|
31
|
-
name: str
|
32
|
-
actions: list[ActionMethod] = field(default_factory=list)
|
33
|
-
icon: Icon | type[Enum] = None
|
34
|
-
icon_only: bool = False
|
35
|
-
|
36
|
-
|
37
28
|
@dataclass
|
38
29
|
class ClientAction:
|
39
30
|
|
@@ -51,6 +42,15 @@ class ClientAction:
|
|
51
42
|
return ' '.join([f'{str(attr[0])}={str(attr[1])}' for attr in self.attrs])
|
52
43
|
|
53
44
|
|
45
|
+
@dataclass
|
46
|
+
class ClientActionGroup:
|
47
|
+
|
48
|
+
name: str
|
49
|
+
actions: list[ClientAction] = field(default_factory=list)
|
50
|
+
icon: Icon | type[Enum] = None
|
51
|
+
icon_only: bool = False
|
52
|
+
|
53
|
+
|
54
54
|
class TableFieldAlignment(Enum):
|
55
55
|
|
56
56
|
LEFT = 'left'
|
accrete/contrib/ui/filter.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import time
|
2
1
|
import logging
|
3
2
|
from django.db.models.fields.related import ManyToOneRel, ManyToManyRel
|
4
3
|
from django.core.cache import cache
|
@@ -301,10 +300,11 @@ class Filter:
|
|
301
300
|
and not isinstance(x, (ManyToOneRel, ManyToManyRel)),
|
302
301
|
self.model._meta.get_fields()
|
303
302
|
)]
|
304
|
-
|
305
|
-
(
|
306
|
-
|
307
|
-
|
303
|
+
if hasattr(self.model, 'get_annotations'):
|
304
|
+
fields.extend([
|
305
|
+
(x['annotation'].verbose_name, x['name'])
|
306
|
+
for x in self.model.get_annotations()
|
307
|
+
])
|
308
308
|
sorted_fields = sorted(fields, key=lambda x: x[0].lower())
|
309
309
|
for field in sorted_fields:
|
310
310
|
html += f"""
|
@@ -20928,12 +20928,6 @@ select:focus {
|
|
20928
20928
|
border-bottom-right-radius: 0;
|
20929
20929
|
}
|
20930
20930
|
|
20931
|
-
#list-customization * .dropdown-menu {
|
20932
|
-
width: 292px;
|
20933
|
-
border-top-left-radius: 0;
|
20934
|
-
border-top-right-radius: 0;
|
20935
|
-
}
|
20936
|
-
|
20937
20931
|
@media screen and (max-width: 768px) {
|
20938
20932
|
table.has-box * td {
|
20939
20933
|
width: 100%;
|
@@ -20961,6 +20955,23 @@ select:focus {
|
|
20961
20955
|
display: inline-block;
|
20962
20956
|
}
|
20963
20957
|
}
|
20958
|
+
/* Chrome, Safari, Edge, Opera */
|
20959
|
+
input::-webkit-outer-spin-button,
|
20960
|
+
input::-webkit-inner-spin-button {
|
20961
|
+
-webkit-appearance: none;
|
20962
|
+
margin: 0;
|
20963
|
+
}
|
20964
|
+
|
20965
|
+
/* Firefox */
|
20966
|
+
input[type=number] {
|
20967
|
+
-moz-appearance: textfield;
|
20968
|
+
}
|
20969
|
+
|
20970
|
+
/* Fix CKEditor List Display */
|
20971
|
+
.ck-editor__editable_inline {
|
20972
|
+
padding: 0 30px !important;
|
20973
|
+
}
|
20974
|
+
|
20964
20975
|
@media screen and (max-width: 1407px) {
|
20965
20976
|
#list-customization * .dropdown-menu {
|
20966
20977
|
width: auto;
|