accrete 0.0.53__py3-none-any.whl → 0.0.55__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 +59 -8
- accrete/contrib/ui/filter.py +5 -5
- accrete/contrib/ui/static/css/accrete.css +20 -4
- accrete/contrib/ui/static/css/accrete.css.map +1 -1
- accrete/contrib/ui/static/css/accrete.scss +34 -4
- accrete/contrib/ui/static/js/ui.js +26 -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 +11 -24
- accrete/contrib/ui/templates/ui/list.html +2 -0
- accrete/contrib/ui/templates/ui/partials/filter.html +32 -23
- accrete/contrib/ui/templates/ui/partials/form_errors.html +33 -20
- accrete/contrib/ui/templates/ui/table.html +2 -0
- 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.53.dist-info → accrete-0.0.55.dist-info}/METADATA +1 -1
- {accrete-0.0.53.dist-info → accrete-0.0.55.dist-info}/RECORD +24 -23
- {accrete-0.0.53.dist-info → accrete-0.0.55.dist-info}/WHEEL +1 -1
- accrete/contrib/ui/static/js/list.js +0 -146
- {accrete-0.0.53.dist-info → accrete-0.0.55.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)
|
@@ -155,6 +197,15 @@ def detail_page(queryset: QuerySet, obj: Model) -> dict:
|
|
155
197
|
'deactivate pagination.'
|
156
198
|
)
|
157
199
|
return {}
|
200
|
+
if obj not in queryset:
|
201
|
+
url = obj.get_absolute_url()
|
202
|
+
return {
|
203
|
+
'previous_object_url': url,
|
204
|
+
'next_object_url': url,
|
205
|
+
'current_object_idx': 1,
|
206
|
+
'total_objects': 1
|
207
|
+
}
|
208
|
+
|
158
209
|
idx = (*queryset,).index(obj)
|
159
210
|
previous_object_url = (
|
160
211
|
queryset[idx - 1] if idx - 1 >= 0 else queryset.last()
|
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"""
|
@@ -20923,10 +20923,9 @@ select:focus {
|
|
20923
20923
|
border-bottom-right-radius: 0;
|
20924
20924
|
}
|
20925
20925
|
|
20926
|
-
|
20927
|
-
|
20928
|
-
border-
|
20929
|
-
border-top-right-radius: 0;
|
20926
|
+
.dropdown-button.is-active {
|
20927
|
+
border-bottom-left-radius: 0;
|
20928
|
+
border-bottom-right-radius: 0;
|
20930
20929
|
}
|
20931
20930
|
|
20932
20931
|
@media screen and (max-width: 768px) {
|
@@ -20956,6 +20955,23 @@ select:focus {
|
|
20956
20955
|
display: inline-block;
|
20957
20956
|
}
|
20958
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
|
+
|
20959
20975
|
@media screen and (max-width: 1407px) {
|
20960
20976
|
#list-customization * .dropdown-menu {
|
20961
20977
|
width: auto;
|