django-unfold 0.22.0__py3-none-any.whl → 0.24.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- {django_unfold-0.22.0.dist-info → django_unfold-0.24.0.dist-info}/METADATA +102 -4
- {django_unfold-0.22.0.dist-info → django_unfold-0.24.0.dist-info}/RECORD +44 -43
- unfold/admin.py +29 -22
- unfold/apps.py +5 -0
- unfold/checks.py +1 -1
- unfold/contrib/filters/admin.py +116 -0
- unfold/contrib/filters/forms.py +29 -1
- unfold/contrib/filters/templates/unfold/filters/filters_date_range.html +1 -1
- unfold/contrib/filters/templates/unfold/filters/filters_datetime_range.html +1 -1
- unfold/contrib/filters/templates/unfold/filters/filters_field.html +7 -0
- unfold/contrib/filters/templates/unfold/filters/filters_numeric_range.html +1 -1
- unfold/contrib/filters/templates/unfold/filters/filters_numeric_single.html +1 -1
- unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +1 -1
- unfold/contrib/import_export/admin.py +1 -1
- unfold/contrib/import_export/forms.py +6 -7
- unfold/contrib/import_export/templates/admin/import_export/export.html +1 -1
- unfold/contrib/import_export/templates/admin/import_export/import_form.html +2 -2
- unfold/forms.py +6 -1
- unfold/sites.py +3 -3
- unfold/static/unfold/css/styles.css +1 -1
- unfold/styles.css +11 -4
- unfold/templates/admin/actions.html +9 -9
- unfold/templates/admin/app_list.html +6 -8
- unfold/templates/admin/base.html +1 -3
- unfold/templates/admin/change_form.html +1 -2
- unfold/templates/admin/change_list.html +2 -2
- unfold/templates/admin/change_list_results.html +15 -3
- unfold/templates/admin/edit_inline/tabular.html +3 -3
- unfold/templates/admin/filter.html +2 -2
- unfold/templates/admin/search_form.html +10 -12
- unfold/templates/unfold/helpers/display_header.html +16 -13
- unfold/templates/unfold/helpers/tab_list.html +1 -1
- unfold/templates/unfold/layouts/base_simple.html +1 -1
- unfold/templates/unfold/widgets/date.html +1 -1
- unfold/templates/{admin → unfold}/widgets/related_widget_wrapper.html +4 -4
- unfold/templates/unfold/widgets/time.html +1 -1
- unfold/templatetags/unfold_list.py +8 -6
- unfold/widgets.py +18 -5
- {django_unfold-0.22.0.dist-info → django_unfold-0.24.0.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.22.0.dist-info → django_unfold-0.24.0.dist-info}/WHEEL +0 -0
- /unfold/templates/{admin → unfold}/widgets/clearable_file_input.html +0 -0
- /unfold/templates/{admin → unfold}/widgets/radio.html +0 -0
- /unfold/templates/{admin → unfold}/widgets/radio_option.html +0 -0
- /unfold/templates/{admin → unfold}/widgets/split_datetime.html +0 -0
@@ -8,10 +8,10 @@
|
|
8
8
|
|
9
9
|
{% if results %}
|
10
10
|
<table id="result_list" class="block border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-400 lg:border lg:rounded-md lg:shadow-sm lg:table lg:dark:border-gray-800">
|
11
|
-
<thead
|
11
|
+
<thead>
|
12
12
|
<tr>
|
13
13
|
{% for header in result_headers %}
|
14
|
-
<th class="align-middle font-medium
|
14
|
+
<th class="align-middle font-medium py-2 text-left text-gray-400 text-sm {{ header.class_attrib }} {% if "action-toggle" in header.text and forloop.counter == 1 %}lg:px-3 lg:w-10{% else %}hidden px-3 lg:table-cell{% endif %}" scope="col">
|
15
15
|
<div class="flex items-center">
|
16
16
|
<div class="text">
|
17
17
|
{% if header.sortable %}
|
@@ -19,7 +19,19 @@
|
|
19
19
|
{{ header.text|capfirst }}
|
20
20
|
</a>
|
21
21
|
{% else %}
|
22
|
-
|
22
|
+
{% if "action-toggle" in header.text and forloop.counter == 1 %}
|
23
|
+
<label class="flex flex-row items-center gap-2">
|
24
|
+
{{ header.text|capfirst }}
|
25
|
+
|
26
|
+
<span class="block font-normal text-gray-500 dark:text-gray-400 lg:hidden">
|
27
|
+
{% trans "Select all rows"%}
|
28
|
+
</span>
|
29
|
+
</label>
|
30
|
+
{% else %}
|
31
|
+
<span>
|
32
|
+
{{ header.text|capfirst }}
|
33
|
+
</span>
|
34
|
+
{% endif %}
|
23
35
|
{% endif %}
|
24
36
|
</div>
|
25
37
|
|
@@ -109,11 +109,11 @@
|
|
109
109
|
{% with is_last_col=forloop.last %}
|
110
110
|
{% for field in line %}
|
111
111
|
{% if field.is_readonly or not field.field.is_hidden %}
|
112
|
-
<td{% if field.field.name %} class="field-{{ field.field.name }}{% if field.field.errors|length > 0 %} errors{% endif %}{% if inline_admin_form.original %} p-3 lg:py-3{% else %} py-3{% endif %}{% if field.is_checkbox %} align-middle{% else %} align-top{% endif %} {% if is_last_row and not inline_admin_formset.has_add_permission %}{% if is_last_col %}border-0 {% else %}border-b lg:border-0{% endif %}{% else %}border-b{% endif %} border-gray-200 flex items-center before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 before:w-72 lg:before:hidden font-normal px-3 text-left text-sm lg:table-cell dark:border-gray-800"{% endif %} data-label="{{ field.field.label }}">
|
112
|
+
<td{% if field.field.name %} class="field-{{ field.field.name }}{% if field.field.errors|length > 0 %} errors{% endif %}{% if inline_admin_form.original %} p-3 lg:py-3{% else %} py-3{% endif %}{% if field.is_checkbox %} align-middle{% else %} align-top{% endif %} {% if is_last_row and not inline_admin_formset.has_add_permission %}{% if is_last_col %}border-0 {% else %}border-b lg:border-0{% endif %}{% else %}border-b{% endif %} border-gray-200 flex items-center before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 before:w-72 lg:before:hidden font-normal px-3 text-left text-sm lg:table-cell dark:border-gray-800 {% if field.field.is_hidden %} !hidden{% endif %}"{% endif %} data-label="{{ field.field.label }}">
|
113
113
|
{% if field.is_readonly %}
|
114
|
-
<
|
114
|
+
<div class="bg-gray-50 border font-medium max-w-lg px-3 py-2 rounded-md shadow-sm text-gray-500 text-sm truncate whitespace-nowrap dark:border-gray-700 dark:text-gray-400 dark:bg-gray-800">
|
115
115
|
{{ field.contents }}
|
116
|
-
</
|
116
|
+
</div>
|
117
117
|
{% else %}
|
118
118
|
{{ field.field }}
|
119
119
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
{% load i18n unfold %}
|
2
2
|
|
3
3
|
<div class="mb-6">
|
4
|
-
<h3 class="font-medium mb-
|
4
|
+
<h3 class="font-medium mb-2 text-gray-700 text-sm dark:text-gray-200">
|
5
5
|
{% blocktranslate with filter_title=title %} By {{ filter_title }} {% endblocktranslate %}
|
6
6
|
</h3>
|
7
7
|
|
8
8
|
{% for choice in choices %}
|
9
|
-
{% if choice.selected %}
|
9
|
+
{% if choice.selected and spec.lookup_val.0 %}
|
10
10
|
<input type="hidden" name="{{ spec.lookup_kwarg }}" value="{{ spec.lookup_val.0 }}" />
|
11
11
|
{% endif %}
|
12
12
|
{% endfor %}
|
@@ -1,19 +1,17 @@
|
|
1
1
|
{% load i18n static %}
|
2
2
|
|
3
3
|
{% if cl.search_fields %}
|
4
|
-
<div id="toolbar"
|
5
|
-
<div>
|
6
|
-
<
|
7
|
-
<input class="font-medium h-9 px-3 text-gray-500 text-sm w-40 lg:w-60 focus:outline-none dark:bg-gray-900 dark:text-gray-400" type="text" name="{{ search_var }}" value="{{ cl.query }}" id="searchbar" placeholder="{% trans 'Type to search' %}" />
|
4
|
+
<div id="toolbar">
|
5
|
+
<div class="bg-white border flex rounded-md overflow-hidden shadow-sm lg:w-64 focus-within:ring focus-within:ring-primary-300 focus-within:border-primary-600 dark:bg-gray-900 dark:border-gray-700 dark:focus-within:border-primary-600 dark:focus-within:ring-primary-700 dark:focus-within:ring-opacity-50">
|
6
|
+
<input class="font-medium grow min-w-0 h-9 px-3 text-gray-500 text-sm focus:outline-none dark:bg-gray-900 dark:text-gray-400" type="text" name="{{ search_var }}" value="{{ cl.query }}" id="searchbar" placeholder="{% trans 'Type to search' %}" />
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
</div>
|
13
|
-
|
14
|
-
{% for pair in cl.params.items %}
|
15
|
-
{% if pair.0 != search_var %}<input type="hidden" name="{{ pair.0 }}" value="{{ pair.1 }}">{% endif %}
|
16
|
-
{% endfor %}
|
8
|
+
<button type="submit" class="flex items-center px-2 focus:outline-none" id="searchbar-submit">
|
9
|
+
<span class="material-symbols-outlined md-18 text-gray-400 dark:text-gray-500">search</span>
|
10
|
+
</button>
|
17
11
|
</div>
|
12
|
+
|
13
|
+
{% for pair in cl.params.items %}
|
14
|
+
{% if pair.0 != search_var %}<input type="hidden" name="{{ pair.0 }}" value="{{ pair.1 }}">{% endif %}
|
15
|
+
{% endfor %}
|
18
16
|
</div>
|
19
17
|
{% endif %}
|
@@ -1,17 +1,20 @@
|
|
1
1
|
<div class="flex gap-4 items-center">
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
{% if value.3 and value.3.path %}
|
3
|
+
<div class="bg-center bg-cover bg-white border flex font-medium h-8 w-8 dark:bg-gray-900 dark:border-gray-700 {% if value.3.squared %}rounded-sm{% else %}rounded-full{% endif %}" style="background-image: url('{{ value.3.path }}')">
|
4
|
+
</div>
|
5
|
+
{% elif value.2 %}
|
6
|
+
<div class="bg-white border flex font-medium h-8 justify-center items-center rounded-full text-xs uppercase w-8 dark:bg-gray-900 dark:border-gray-700">
|
7
|
+
{{ value.2 }}
|
8
|
+
</div>
|
9
|
+
{% endif %}
|
7
10
|
|
8
|
-
|
9
|
-
|
11
|
+
<div class="flex flex-col text-right lg:text-left">
|
12
|
+
<h3>{{ value.0 }}</h3>
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
{% if value.1 %}
|
15
|
+
<p class="text-gray-500">
|
16
|
+
{{ value.1 }}
|
17
|
+
</p>
|
18
|
+
{% endif %}
|
19
|
+
</div>
|
17
20
|
</div>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
{% if not is_popup %}
|
2
2
|
{% if tab_list or actions_list or actions_items or nav_global %}
|
3
|
-
<div class="flex items-start flex-col mb-
|
3
|
+
<div class="flex items-start flex-col mb-4 text-gray-500 text-sm w-full md:border-b dark:md:border-gray-800 md:border-l-0 md:flex-row md:items-center md:justify-end dark:text-gray-400">
|
4
4
|
{% if tab_list %}
|
5
5
|
<ul class="border rounded-md flex flex-col w-full md:flex-row md:border-b-0 md:border-t-0 md:border-l-0 md:border-r-0 dark:border-gray-800">
|
6
6
|
{% for item in tab_list %}
|
@@ -9,7 +9,7 @@
|
|
9
9
|
{% if not is_hidden %}
|
10
10
|
{% if can_add_related or can_change_related or can_delete_related or can_view_related%}
|
11
11
|
{% if can_change_related %}
|
12
|
-
<a class="related-widget-wrapper-link change-related border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-gray-400 text-sm
|
12
|
+
<a class="related-widget-wrapper-link change-related bg-white border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-gray-400 text-sm w-9.5 hover:text-gray-700 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-500 dark:hover:text-gray-200"
|
13
13
|
id="change_id_{{ name }}"
|
14
14
|
data-popup="yes"
|
15
15
|
data-href-template="{{ change_related_template_url }}?{{ url_params }}"
|
@@ -19,7 +19,7 @@
|
|
19
19
|
{% endif %}
|
20
20
|
|
21
21
|
{% if can_add_related %}
|
22
|
-
<a class="related-widget-wrapper-link add-related border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-gray-400 text-sm
|
22
|
+
<a class="related-widget-wrapper-link add-related bg-white border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-gray-400 text-sm w-9.5 hover:text-gray-700 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-500 dark:hover:text-gray-200"
|
23
23
|
data-popup="yes"
|
24
24
|
id="add_id_{{ name }}"
|
25
25
|
href="{{ add_related_url }}?{{ url_params }}"
|
@@ -29,7 +29,7 @@
|
|
29
29
|
{% endif %}
|
30
30
|
|
31
31
|
{% if can_view_related %}
|
32
|
-
<a class="related-widget-wrapper-link view-related border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-gray-400 text-sm
|
32
|
+
<a class="related-widget-wrapper-link view-related bg-white border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-gray-400 text-sm w-9.5 hover:text-gray-700 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-500 dark:hover:text-gray-200"
|
33
33
|
id="view_id_{{ name }}"
|
34
34
|
data-href-template="{{ change_related_template_url }}?{{ view_related_url_params }}"
|
35
35
|
title="{% blocktranslate %}View selected {{ model }}{% endblocktranslate %}">
|
@@ -38,7 +38,7 @@
|
|
38
38
|
{% endif %}
|
39
39
|
|
40
40
|
{% if can_delete_related %}
|
41
|
-
<a class="related-widget-wrapper-link delete-related border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-red-600 text-sm
|
41
|
+
<a class="related-widget-wrapper-link delete-related bg-white border cursor-pointer flex items-center h-9.5 justify-center ml-2 rounded shadow-sm shrink-0 text-red-600 text-sm w-9.5 dark:bg-gray-900 dark:border-gray-700 dark:text-red-500"
|
42
42
|
id="delete_id_{{ name }}"
|
43
43
|
data-href-template="{{ delete_related_template_url }}?{{ url_params }}"
|
44
44
|
data-popup="yes"
|
@@ -37,9 +37,9 @@ from ..widgets import UnfoldBooleanWidget
|
|
37
37
|
register = Library()
|
38
38
|
|
39
39
|
LINK_CLASSES = [
|
40
|
-
"text-gray-
|
40
|
+
"text-gray-600",
|
41
41
|
"truncate",
|
42
|
-
"dark:text-gray-
|
42
|
+
"dark:text-gray-300",
|
43
43
|
]
|
44
44
|
|
45
45
|
ROW_CLASSES = [
|
@@ -54,6 +54,7 @@ ROW_CLASSES = [
|
|
54
54
|
"px-3",
|
55
55
|
"py-2",
|
56
56
|
"text-left",
|
57
|
+
"text-gray-500",
|
57
58
|
"text-sm",
|
58
59
|
"before:flex",
|
59
60
|
"before:capitalize",
|
@@ -63,6 +64,7 @@ ROW_CLASSES = [
|
|
63
64
|
"before:text-gray-500",
|
64
65
|
"first:border-t-0",
|
65
66
|
"dark:before:text-gray-400",
|
67
|
+
"dark:text-gray-400",
|
66
68
|
"lg:before:hidden",
|
67
69
|
"lg:first:border-t",
|
68
70
|
"lg:py-3",
|
@@ -149,7 +151,7 @@ def result_headers(cl):
|
|
149
151
|
if is_sorted:
|
150
152
|
order_type = ordering_field_columns.get(i).lower()
|
151
153
|
sort_priority = list(ordering_field_columns).index(i) + 1
|
152
|
-
th_classes.append("sorted
|
154
|
+
th_classes.append(f"sorted {order_type}ending")
|
153
155
|
new_order_type = {"asc": "desc", "desc": "asc"}[order_type]
|
154
156
|
|
155
157
|
# build new ordering param
|
@@ -186,7 +188,7 @@ def result_headers(cl):
|
|
186
188
|
"url_primary": cl.get_query_string({ORDER_VAR: ".".join(o_list_primary)}),
|
187
189
|
"url_remove": cl.get_query_string({ORDER_VAR: ".".join(o_list_remove)}),
|
188
190
|
"url_toggle": cl.get_query_string({ORDER_VAR: ".".join(o_list_toggle)}),
|
189
|
-
"class_attrib": format_html(
|
191
|
+
"class_attrib": format_html("{}", " ".join(th_classes))
|
190
192
|
if th_classes
|
191
193
|
else "",
|
192
194
|
}
|
@@ -211,7 +213,7 @@ def items_for_result(cl: ChangeList, result: HttpRequest, form) -> SafeText:
|
|
211
213
|
for field_index, field_name in enumerate(cl.list_display):
|
212
214
|
empty_value_display = cl.model_admin.get_empty_value_display()
|
213
215
|
row_classes = [
|
214
|
-
"field
|
216
|
+
f"field-{_coerce_field_name(field_name, field_index)}",
|
215
217
|
*ROW_CLASSES,
|
216
218
|
]
|
217
219
|
|
@@ -252,7 +254,7 @@ def items_for_result(cl: ChangeList, result: HttpRequest, form) -> SafeText:
|
|
252
254
|
f, (models.DateField, models.TimeField, models.ForeignKey)
|
253
255
|
):
|
254
256
|
row_classes.append("nowrap")
|
255
|
-
row_class = mark_safe(' class="
|
257
|
+
row_class = mark_safe(f' class="{" ".join(row_classes)}"')
|
256
258
|
# If list_display_links not defined, add the link tag to the first field
|
257
259
|
|
258
260
|
if link_in_col(first, field_name, cl):
|
unfold/widgets.py
CHANGED
@@ -13,6 +13,7 @@ from django.contrib.admin.widgets import (
|
|
13
13
|
AdminTextInputWidget,
|
14
14
|
AdminTimeWidget,
|
15
15
|
AdminUUIDInputWidget,
|
16
|
+
RelatedFieldWidgetWrapper,
|
16
17
|
)
|
17
18
|
from django.forms import (
|
18
19
|
CheckboxInput,
|
@@ -291,7 +292,7 @@ class FileFieldMixin:
|
|
291
292
|
|
292
293
|
|
293
294
|
class UnfoldAdminImageFieldWidget(FileFieldMixin, AdminFileWidget):
|
294
|
-
|
295
|
+
template_name = "unfold/widgets/clearable_file_input.html"
|
295
296
|
|
296
297
|
|
297
298
|
class UnfoldAdminFileFieldWidget(FileFieldMixin, AdminFileWidget):
|
@@ -374,6 +375,8 @@ class UnfoldAdminTextareaWidget(AdminTextareaWidget):
|
|
374
375
|
|
375
376
|
|
376
377
|
class UnfoldAdminSplitDateTimeWidget(AdminSplitDateTime):
|
378
|
+
template_name = "unfold/widgets/split_datetime.html"
|
379
|
+
|
377
380
|
def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
|
378
381
|
widgets = [UnfoldAdminDateWidget, UnfoldAdminTimeWidget]
|
379
382
|
MultiWidget.__init__(self, widgets, attrs)
|
@@ -433,10 +436,15 @@ class UnfoldAdminBigIntegerFieldWidget(AdminBigIntegerFieldWidget):
|
|
433
436
|
|
434
437
|
|
435
438
|
class UnfoldAdminNullBooleanSelectWidget(NullBooleanSelect):
|
436
|
-
|
439
|
+
def __init__(self, attrs=None):
|
440
|
+
if attrs is None:
|
441
|
+
attrs = {}
|
437
442
|
|
443
|
+
attrs["class"] = " ".join(SELECT_CLASSES)
|
444
|
+
super().__init__(attrs)
|
438
445
|
|
439
|
-
|
446
|
+
|
447
|
+
class UnfoldAdminSelectWidget(Select):
|
440
448
|
def __init__(self, attrs=None, choices=()):
|
441
449
|
if attrs is None:
|
442
450
|
attrs = {}
|
@@ -446,7 +454,8 @@ class UnfoldAdminSelect(Select):
|
|
446
454
|
|
447
455
|
|
448
456
|
class UnfoldAdminRadioSelectWidget(AdminRadioSelect):
|
449
|
-
|
457
|
+
template_name = "unfold/widgets/radio.html"
|
458
|
+
option_template_name = "unfold/widgets/radio_option.html"
|
450
459
|
|
451
460
|
def __init__(self, radio_style: Optional[int] = None, *args, **kwargs):
|
452
461
|
super().__init__(*args, **kwargs)
|
@@ -473,7 +482,7 @@ try:
|
|
473
482
|
def __init__(self, *args, **kwargs):
|
474
483
|
super().__init__(
|
475
484
|
amount_widget=UnfoldAdminTextInputWidget,
|
476
|
-
currency_widget=
|
485
|
+
currency_widget=UnfoldAdminSelectWidget(choices=CURRENCY_CHOICES),
|
477
486
|
)
|
478
487
|
|
479
488
|
except ImportError:
|
@@ -506,3 +515,7 @@ class UnfoldBooleanSwitchWidget(CheckboxInput):
|
|
506
515
|
return super().__init__(
|
507
516
|
attrs={"class": " ".join(SWITCH_CLASSES), **(attrs or {})}, check_test=None
|
508
517
|
)
|
518
|
+
|
519
|
+
|
520
|
+
class UnfoldRelatedFieldWidgetWrapper(RelatedFieldWidgetWrapper):
|
521
|
+
template_name = "unfold/widgets/related_widget_wrapper.html"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|