django-unfold 0.34.0__py3-none-any.whl → 0.35.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.34.0.dist-info → django_unfold-0.35.0.dist-info}/METADATA +17 -5
- {django_unfold-0.34.0.dist-info → django_unfold-0.35.0.dist-info}/RECORD +13 -12
- unfold/admin.py +7 -5
- unfold/contrib/filters/admin.py +34 -1
- unfold/contrib/filters/forms.py +36 -3
- unfold/decorators.py +2 -2
- unfold/static/unfold/js/app.js +23 -0
- unfold/static/unfold/js/select2.init.js +15 -0
- unfold/templates/admin/change_form.html +1 -1
- unfold/templates/admin/submit_line.html +1 -1
- unfold/widgets.py +11 -1
- {django_unfold-0.34.0.dist-info → django_unfold-0.35.0.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.34.0.dist-info → django_unfold-0.35.0.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: django-unfold
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.35.0
|
4
4
|
Summary: Modern Django admin theme for seamless interface development
|
5
5
|
Home-page: https://unfoldadmin.com
|
6
6
|
License: MIT
|
@@ -179,7 +179,7 @@ class CustomAdminClass(ModelAdmin):
|
|
179
179
|
from django.contrib import admin
|
180
180
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
181
181
|
from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
|
182
|
-
from django.contrib.auth.models import User
|
182
|
+
from django.contrib.auth.models import User, Group
|
183
183
|
|
184
184
|
from unfold.admin import ModelAdmin
|
185
185
|
|
@@ -360,6 +360,9 @@ class CustomAdminClass(ModelAdmin):
|
|
360
360
|
# Display fields in changeform in compressed mode
|
361
361
|
compressed_fields = True # Default: False
|
362
362
|
|
363
|
+
# Warn before leaving unsaved changes in changeform
|
364
|
+
warn_unsaved_form = True # Default: False
|
365
|
+
|
363
366
|
# Preprocess content of readonly fields before render
|
364
367
|
readonly_preprocess_fields = {
|
365
368
|
"model_field_name": "html.unescape",
|
@@ -645,7 +648,14 @@ The difference between them is that `ChoicesDropdownFilter` will collect a list
|
|
645
648
|
from django.contrib import admin
|
646
649
|
from django.contrib.auth.models import User
|
647
650
|
from unfold.admin import ModelAdmin
|
648
|
-
from unfold.contrib.filters.admin import
|
651
|
+
from unfold.contrib.filters.admin import (
|
652
|
+
ChoicesDropdownFilter,
|
653
|
+
MultipleChoicesDropdownFilter,
|
654
|
+
RelatedDropdownFilter,
|
655
|
+
MultipleRelatedDropdownFilter,
|
656
|
+
DropdownFilter,
|
657
|
+
MultipleDropdownFilter
|
658
|
+
)
|
649
659
|
|
650
660
|
|
651
661
|
class CustomDropdownFilter(DropdownFilter):
|
@@ -672,7 +682,9 @@ class MyAdmin(ModelAdmin):
|
|
672
682
|
list_filter = [
|
673
683
|
CustomDropdownFilter,
|
674
684
|
("modelfield_with_choices", ChoicesDropdownFilter),
|
685
|
+
("modelfield_with_choices_multiple", MultipleChoicesDropdownFilter),
|
675
686
|
("modelfield_with_foreign_key", RelatedDropdownFilter)
|
687
|
+
("modelfield_with_foreign_key_multiple", MultipleRelatedDropdownFilter)
|
676
688
|
]
|
677
689
|
```
|
678
690
|
|
@@ -1363,8 +1375,8 @@ def dashboard_callback(request: HttpRequest) -> Dict:
|
|
1363
1375
|
```
|
1364
1376
|
|
1365
1377
|
```django-html
|
1366
|
-
{% component "unfold/components/card" with title="Card title" %}
|
1367
|
-
{% component "unfold/components/table.html" with table=table_data card_included=1 striped=1 %}{%
|
1378
|
+
{% component "unfold/components/card.html" with title="Card title" %}
|
1379
|
+
{% component "unfold/components/table.html" with table=table_data card_included=1 striped=1 %}{% endcomponent %}
|
1368
1380
|
{% endcomponent %}
|
1369
1381
|
```
|
1370
1382
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
unfold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
unfold/admin.py,sha256=
|
2
|
+
unfold/admin.py,sha256=nUAouLLyo57CE3EpOREHDMjOaj68P7MZO6KEPYlutYU,19587
|
3
3
|
unfold/apps.py,sha256=SlBXPYrUd2uXn67qFbRvbXSUk3XFWrF4-5WELgDCvho,381
|
4
4
|
unfold/checks.py,sha256=Smgji9w19hnYjJElJ_FJnnyTEAE-E-OUB6otHu7lasY,1670
|
5
5
|
unfold/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
unfold/contrib/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
unfold/contrib/filters/admin.py,sha256=
|
7
|
+
unfold/contrib/filters/admin.py,sha256=4dwK-LCXaopQeqW3818wUiZJhkJEDLNwbDLJboVJPHk,21138
|
8
8
|
unfold/contrib/filters/apps.py,sha256=wEySJy0gMLzFLb9XNKE-RexiO05X7NaQ5QmxZyziJ_k,136
|
9
|
-
unfold/contrib/filters/forms.py,sha256=
|
9
|
+
unfold/contrib/filters/forms.py,sha256=N25YiD7YfBxg57-goSnaXc-eyEQFp1XYgDmmQwg7jQ0,5696
|
10
10
|
unfold/contrib/filters/static/unfold/filters/css/nouislider.min.css,sha256=rddL_jOGGVEY6wR-aw0VYovAfz5fPeAIsulrlSNb1hc,4221
|
11
11
|
unfold/contrib/filters/static/unfold/filters/js/DateTimeShortcuts.js,sha256=jgFNBDf6aHvUlyv0LEDHggXO-xA8pWOCWFWcVupdA30,19332
|
12
12
|
unfold/contrib/filters/static/unfold/filters/js/admin-numeric-filter.js,sha256=nTkiiJk4Abn9d6KigxPSEsereFhFq-v5n_boiiY1eII,1668
|
@@ -65,7 +65,7 @@ unfold/contrib/simple_history/templates/simple_history/object_history_form.html,
|
|
65
65
|
unfold/contrib/simple_history/templates/simple_history/object_history_list.html,sha256=f0RxTiPfC957uQzNfSkKoyMUzu8LRw4-MS6d3xZ2gew,6821
|
66
66
|
unfold/contrib/simple_history/templates/simple_history/submit_line.html,sha256=9v7PfS8M6N0NccTEb_9QyTG28NgDBiIkjuBOfWGIQ18,1703
|
67
67
|
unfold/dataclasses.py,sha256=4PAWLVlUhyGMq1Mwai7afCgrRO0Inulw0opFUKVEOBk,483
|
68
|
-
unfold/decorators.py,sha256=
|
68
|
+
unfold/decorators.py,sha256=jyjE2sunazp8gK1QIsNRJGEEDwO7RDKWDKnOI3GDWZE,3338
|
69
69
|
unfold/exceptions.py,sha256=gcCj1ox61E137bk_0Cqy4YC3SttdPgB-fiJUqpmyHSE,43
|
70
70
|
unfold/fields.py,sha256=yhtpDfqycpOxqdQlbncCg9qhELxGk3AtXizkZyzzH0g,7410
|
71
71
|
unfold/forms.py,sha256=8-IoFZLwUNJKTNdev_oJwdxm-_xekvrn_84IXKse4Q8,4100
|
@@ -83,9 +83,10 @@ unfold/static/unfold/fonts/material-symbols/styles.css,sha256=U0oiGd2DS7LXnbuqgs
|
|
83
83
|
unfold/static/unfold/js/alpine.anchor.js,sha256=2Jg9aUq749pjFynzr_H1NK3lf-nXrbHMtO9wvlWJyIQ,15524
|
84
84
|
unfold/static/unfold/js/alpine.js,sha256=NY2a-7GrW--i9IBhowd25bzXcH9BCmBrqYX5i8OxwDQ,44659
|
85
85
|
unfold/static/unfold/js/alpine.persist.js,sha256=jFBwr6faTqqhp3sVi4_VTxJ0FpaF9YGZN1ZGLl_5QYM,837
|
86
|
-
unfold/static/unfold/js/app.js,sha256=
|
86
|
+
unfold/static/unfold/js/app.js,sha256=kL7gJu4bDcY7v61KYtYtjn7KxWd-rHVvL49GTD_q770,6884
|
87
87
|
unfold/static/unfold/js/chart.js,sha256=22W6cFERR-CElMOKRgMMicueMVP0Vf7FBEBYH8Z8tCk,200633
|
88
88
|
unfold/static/unfold/js/htmx.js,sha256=XOLqvnZiyEx46EW9vaJTBUaaWg8CGVVfXJkVsUmJbpI,42820
|
89
|
+
unfold/static/unfold/js/select2.init.js,sha256=SzpjSL1mD9tHedjSeXlmEwHjuuyqlnpilpSeEEKeYLE,265
|
89
90
|
unfold/static/unfold/js/simplebar.js,sha256=t-uG1FAD6ZoiMeN--wac0XRS7SxoDVG6zvRnGuEp7X8,27176
|
90
91
|
unfold/styles.css,sha256=egNdWhCzaGS5Y_tCiceIU6BTNn1MkrMMooX5zDv_r6I,18223
|
91
92
|
unfold/templates/admin/actions.html,sha256=8GqELAxzywGyfQJiQeuhGIQUakdK_WeneILY06C7mEo,2746
|
@@ -95,7 +96,7 @@ unfold/templates/admin/auth/user/add_form.html,sha256=iLig-vd2YExXsj0xGBwYhZ4kGU
|
|
95
96
|
unfold/templates/admin/auth/user/change_password.html,sha256=-Wa9ml3yss-kDz0YQxCiwoxs91KQD8eetCt5l6xekWM,2892
|
96
97
|
unfold/templates/admin/base.html,sha256=RptiEpgXZ6seYrPu-1Stoe8ECHpi9PjkJjFheGecYF0,2358
|
97
98
|
unfold/templates/admin/base_site.html,sha256=3ckWrcAdd7Pw1hk6Zwyknab_Qb-rteV9-mXhMnfo6VI,361
|
98
|
-
unfold/templates/admin/change_form.html,sha256=
|
99
|
+
unfold/templates/admin/change_form.html,sha256=FiVfbB1j1wb6qGfaLukdMUrLRXsO_sJ4wirKq1bit60,4528
|
99
100
|
unfold/templates/admin/change_form_object_tools.html,sha256=eyeH-i2HgEM0Yi-OJA2D1VnKJyC19A_my1IDGxxoP8Y,593
|
100
101
|
unfold/templates/admin/change_list.html,sha256=o8XnKa1xFot53-BxGeB8afJ0TF8N7TcJhyTA4vPnop0,5124
|
101
102
|
unfold/templates/admin/change_list_object_tools.html,sha256=cmMiT2nT20Ph5yfpj9aHPr76Z-JP4aSXp0o-Rnad28s,147
|
@@ -114,7 +115,7 @@ unfold/templates/admin/nav_sidebar.html,sha256=cKuC28XU9NT17lrCVugn2cHf39kgkoX49
|
|
114
115
|
unfold/templates/admin/object_history.html,sha256=mRD6nbIVmnsz2OG6UsuXdI55bflY3Vs8zbIE9X-Oi74,4816
|
115
116
|
unfold/templates/admin/pagination.html,sha256=KrwOM6gRizfpTMP59TESQaNGcQzkga2k02opl_7BxLo,1130
|
116
117
|
unfold/templates/admin/search_form.html,sha256=vKuruVUEMarx8FPrBp86yqqFtUXfFPzR_9GxhnOCs4U,1173
|
117
|
-
unfold/templates/admin/submit_line.html,sha256=
|
118
|
+
unfold/templates/admin/submit_line.html,sha256=6V9qtE9BecN905-YkI5ZOLY3ZYyb_mhN_p9Zkhv4h8Q,4248
|
118
119
|
unfold/templates/auth/widgets/read_only_password_hash.html,sha256=I08wFPcDr0Gjrg64nlcKiB12Tz2g_nnJXemiy8tkoZg,785
|
119
120
|
unfold/templates/registration/logged_out.html,sha256=4rjAazcxt8ZCqA5bConKRgAk4VrljNTksdtg7D_btrU,1028
|
120
121
|
unfold/templates/registration/password_change_done.html,sha256=i1ZzfTwZHWNWoN9_xHZDdcgLdTOVbTFFD1HUSuG0LkY,1062
|
@@ -194,8 +195,8 @@ unfold/templatetags/unfold_list.py,sha256=q02u7jbMWyDi_6_rOk17W-jW6Yqe9dkuz7Cc_m
|
|
194
195
|
unfold/typing.py,sha256=1P8PWM2oeaceUJtA5j071RbKEBpHYaux441u7Hd6wv4,643
|
195
196
|
unfold/utils.py,sha256=zZdJE4FmwRd7p5a7sJiAoZjBOJitXJduOq7BulyppWM,4803
|
196
197
|
unfold/views.py,sha256=hQCyeeMa9kcJV1IZeeYqj8PGW7J4QWME8n-5n0UGmiU,1003
|
197
|
-
unfold/widgets.py,sha256=
|
198
|
-
django_unfold-0.
|
199
|
-
django_unfold-0.
|
200
|
-
django_unfold-0.
|
201
|
-
django_unfold-0.
|
198
|
+
unfold/widgets.py,sha256=6hQp13LmuhDJ41YxTG70SPJEg0agmHcmUhCtCUwI6SU,16369
|
199
|
+
django_unfold-0.35.0.dist-info/LICENSE.md,sha256=Ltk_quRyyvV3J5v3brtOqmibeZSw2Hrb8bY1W3ya0Ik,1077
|
200
|
+
django_unfold-0.35.0.dist-info/METADATA,sha256=_lkCvpMErEFx3xEWaKWziYEDzsvy4jxiLlfVkJav5u0,57888
|
201
|
+
django_unfold-0.35.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
202
|
+
django_unfold-0.35.0.dist-info/RECORD,,
|
unfold/admin.py
CHANGED
@@ -236,6 +236,7 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
236
236
|
list_disable_select_all = False
|
237
237
|
compressed_fields = False
|
238
238
|
readonly_preprocess_fields = {}
|
239
|
+
warn_unsaved_form = False
|
239
240
|
checks_class = UnfoldModelAdminChecks
|
240
241
|
|
241
242
|
@property
|
@@ -244,13 +245,14 @@ class ModelAdmin(ModelAdminMixin, BaseModelAdmin):
|
|
244
245
|
additional_media = forms.Media()
|
245
246
|
|
246
247
|
for filter in self.list_filter:
|
247
|
-
if
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
filter[1].form_class, "Media"
|
248
|
+
if (
|
249
|
+
isinstance(filter, (tuple, list))
|
250
|
+
and hasattr(filter[1], "form_class")
|
251
|
+
and hasattr(filter[1].form_class, "Media")
|
252
252
|
):
|
253
253
|
additional_media += forms.Media(filter[1].form_class.Media)
|
254
|
+
elif hasattr(filter, "form_class") and hasattr(filter.form_class, "Media"):
|
255
|
+
additional_media += forms.Media(filter.form_class.Media)
|
254
256
|
|
255
257
|
return media + additional_media
|
256
258
|
|
unfold/contrib/filters/admin.py
CHANGED
@@ -41,12 +41,23 @@ class ValueMixin:
|
|
41
41
|
)
|
42
42
|
|
43
43
|
|
44
|
+
class MultiValueMixin:
|
45
|
+
def value(self) -> Optional[List[str]]:
|
46
|
+
return (
|
47
|
+
self.lookup_val
|
48
|
+
if self.lookup_val not in EMPTY_VALUES
|
49
|
+
and isinstance(self.lookup_val, List)
|
50
|
+
and len(self.lookup_val) > 0
|
51
|
+
else self.lookup_val
|
52
|
+
)
|
53
|
+
|
54
|
+
|
44
55
|
class DropdownMixin:
|
45
56
|
template = "unfold/filters/filters_field.html"
|
46
57
|
form_class = DropdownForm
|
47
58
|
all_option = ["", _("All")]
|
48
59
|
|
49
|
-
def queryset(self, request, queryset) -> QuerySet:
|
60
|
+
def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet:
|
50
61
|
if self.value() not in EMPTY_VALUES:
|
51
62
|
return super().queryset(request, queryset)
|
52
63
|
|
@@ -112,11 +123,23 @@ class DropdownFilter(admin.SimpleListFilter):
|
|
112
123
|
name=self.parameter_name,
|
113
124
|
choices=[self.all_option, *self.lookup_choices],
|
114
125
|
data={self.parameter_name: self.value()},
|
126
|
+
multiple=self.multiple if hasattr(self, "multiple") else False,
|
115
127
|
),
|
116
128
|
},
|
117
129
|
)
|
118
130
|
|
119
131
|
|
132
|
+
class MultipleDropdownFilter(DropdownFilter):
|
133
|
+
multiple = True
|
134
|
+
|
135
|
+
def __init__(self, request, params, model, model_admin):
|
136
|
+
self.request = request
|
137
|
+
super().__init__(request, params, model, model_admin)
|
138
|
+
|
139
|
+
def value(self):
|
140
|
+
return self.request.GET.getlist(self.parameter_name)
|
141
|
+
|
142
|
+
|
120
143
|
class ChoicesDropdownFilter(ValueMixin, DropdownMixin, admin.ChoicesFieldListFilter):
|
121
144
|
def choices(self, changelist: ChangeList):
|
122
145
|
choices = [self.all_option, *self.field.flatchoices]
|
@@ -127,10 +150,15 @@ class ChoicesDropdownFilter(ValueMixin, DropdownMixin, admin.ChoicesFieldListFil
|
|
127
150
|
name=self.lookup_kwarg,
|
128
151
|
choices=choices,
|
129
152
|
data={self.lookup_kwarg: self.value()},
|
153
|
+
multiple=self.multiple if hasattr(self, "multiple") else False,
|
130
154
|
),
|
131
155
|
}
|
132
156
|
|
133
157
|
|
158
|
+
class MultipleChoicesDropdownFilter(MultiValueMixin, ChoicesDropdownFilter):
|
159
|
+
multiple = True
|
160
|
+
|
161
|
+
|
134
162
|
class RelatedDropdownFilter(ValueMixin, DropdownMixin, admin.RelatedFieldListFilter):
|
135
163
|
def choices(self, changelist: ChangeList):
|
136
164
|
yield {
|
@@ -139,10 +167,15 @@ class RelatedDropdownFilter(ValueMixin, DropdownMixin, admin.RelatedFieldListFil
|
|
139
167
|
name=self.lookup_kwarg,
|
140
168
|
choices=[self.all_option, *self.lookup_choices],
|
141
169
|
data={self.lookup_kwarg: self.value()},
|
170
|
+
multiple=self.multiple if hasattr(self, "multiple") else False,
|
142
171
|
),
|
143
172
|
}
|
144
173
|
|
145
174
|
|
175
|
+
class MultipleRelatedDropdownFilter(MultiValueMixin, RelatedDropdownFilter):
|
176
|
+
multiple = True
|
177
|
+
|
178
|
+
|
146
179
|
class SingleNumericFilter(admin.FieldListFilter):
|
147
180
|
request = None
|
148
181
|
parameter_name = None
|
unfold/contrib/filters/forms.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
from django import forms
|
2
|
+
from django.forms import ChoiceField, MultipleChoiceField
|
2
3
|
from django.utils.translation import gettext_lazy as _
|
3
4
|
|
4
5
|
from ...widgets import (
|
5
6
|
INPUT_CLASSES,
|
7
|
+
UnfoldAdminSelectMultipleWidget,
|
6
8
|
UnfoldAdminSelectWidget,
|
7
9
|
UnfoldAdminSplitDateTimeVerticalWidget,
|
8
10
|
UnfoldAdminTextInputWidget,
|
@@ -21,15 +23,46 @@ class SearchForm(forms.Form):
|
|
21
23
|
|
22
24
|
|
23
25
|
class DropdownForm(forms.Form):
|
24
|
-
|
26
|
+
widget = UnfoldAdminSelectWidget(
|
27
|
+
attrs={
|
28
|
+
"data-theme": "admin-autocomplete",
|
29
|
+
"class": "admin-autocomplete",
|
30
|
+
}
|
31
|
+
)
|
32
|
+
field = ChoiceField
|
33
|
+
|
34
|
+
def __init__(self, name, label, choices, multiple=False, *args, **kwargs):
|
25
35
|
super().__init__(*args, **kwargs)
|
26
36
|
|
27
|
-
|
37
|
+
if multiple:
|
38
|
+
self.widget = UnfoldAdminSelectMultipleWidget(
|
39
|
+
attrs={
|
40
|
+
"data-theme": "admin-autocomplete",
|
41
|
+
"class": "admin-autocomplete",
|
42
|
+
}
|
43
|
+
)
|
44
|
+
self.field = MultipleChoiceField
|
45
|
+
|
46
|
+
self.fields[name] = self.field(
|
28
47
|
label=label,
|
29
48
|
required=False,
|
30
49
|
choices=choices,
|
31
|
-
widget=
|
50
|
+
widget=self.widget,
|
51
|
+
)
|
52
|
+
|
53
|
+
class Media:
|
54
|
+
js = (
|
55
|
+
"admin/js/vendor/jquery/jquery.js",
|
56
|
+
"admin/js/vendor/select2/select2.full.js",
|
57
|
+
"admin/js/jquery.init.js",
|
58
|
+
"unfold/js/select2.init.js",
|
32
59
|
)
|
60
|
+
css = {
|
61
|
+
"screen": (
|
62
|
+
"admin/css/vendor/select2/select2.css",
|
63
|
+
"admin/css/autocomplete.css",
|
64
|
+
),
|
65
|
+
}
|
33
66
|
|
34
67
|
|
35
68
|
class SingleNumericForm(forms.Form):
|
unfold/decorators.py
CHANGED
@@ -29,12 +29,12 @@ def action(
|
|
29
29
|
getattr(model_admin, f"has_{permission}_permission")
|
30
30
|
for permission in permissions
|
31
31
|
)
|
32
|
-
# TODO add obj parameter into has_permission method call.
|
33
32
|
# Permissions methods have following syntax: has_<some>_permission(self, request, obj=None):
|
34
33
|
# But obj is not examined by default in django admin and it would also require additional
|
35
34
|
# fetch from database, therefore it is not supported yet
|
36
35
|
if not any(
|
37
|
-
has_permission(request)
|
36
|
+
has_permission(request, kwargs.get("object_id"))
|
37
|
+
for has_permission in permission_checks
|
38
38
|
):
|
39
39
|
raise PermissionDenied
|
40
40
|
return func(model_admin, request, *args, **kwargs)
|
unfold/static/unfold/js/app.js
CHANGED
@@ -8,8 +8,31 @@ window.addEventListener("load", (e) => {
|
|
8
8
|
renderCharts();
|
9
9
|
|
10
10
|
filterForm();
|
11
|
+
|
12
|
+
warnWithoutSaving();
|
11
13
|
});
|
12
14
|
|
15
|
+
/*************************************************************
|
16
|
+
* Warn without saving
|
17
|
+
*************************************************************/
|
18
|
+
const warnWithoutSaving = () => {
|
19
|
+
let formChanged = false;
|
20
|
+
|
21
|
+
Array.from(
|
22
|
+
document.querySelectorAll(
|
23
|
+
"form.warn-unsaved-form input, form.warn-unsaved-form select"
|
24
|
+
)
|
25
|
+
).forEach((field) => {
|
26
|
+
field.addEventListener("change", (e) => (formChanged = true));
|
27
|
+
});
|
28
|
+
|
29
|
+
window.addEventListener("beforeunload", (e) => {
|
30
|
+
if (formChanged) {
|
31
|
+
e.preventDefault();
|
32
|
+
}
|
33
|
+
});
|
34
|
+
};
|
35
|
+
|
13
36
|
/*************************************************************
|
14
37
|
* Filter form
|
15
38
|
*************************************************************/
|
@@ -0,0 +1,15 @@
|
|
1
|
+
"use strict";
|
2
|
+
{
|
3
|
+
const $ = django.jQuery;
|
4
|
+
|
5
|
+
$.fn.djangoCustomSelect2 = function () {
|
6
|
+
$.each(this, function (i, element) {
|
7
|
+
$(element).select2();
|
8
|
+
});
|
9
|
+
return this;
|
10
|
+
};
|
11
|
+
|
12
|
+
$(function () {
|
13
|
+
$(".admin-autocomplete").djangoCustomSelect2();
|
14
|
+
});
|
15
|
+
}
|
@@ -59,7 +59,7 @@
|
|
59
59
|
|
60
60
|
{% block content %}
|
61
61
|
<div id="content-main">
|
62
|
-
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}{% if form_url %}action="{{ form_url }}" {% endif %}method="post" id="{{ opts.model_name }}_form" novalidate>
|
62
|
+
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}{% if form_url %}action="{{ form_url }}" {% endif %}method="post" id="{{ opts.model_name }}_form" {% if adminform.model_admin.warn_unsaved_form %}class="warn-unsaved-form"{% endif %} novalidate>
|
63
63
|
{% csrf_token %}
|
64
64
|
|
65
65
|
{% block form_top %}{% endblock %}
|
@@ -42,7 +42,7 @@
|
|
42
42
|
{% endif %}
|
43
43
|
|
44
44
|
{% if show_save_as_new %}
|
45
|
-
<button type="submit" name="_saveasnew" class="border font-medium px-3 py-2 rounded-md transition-all w-full hover:bg-gray-50 dark:border-gray-700 dark:hover:text-gray-200 dark:hover:bg-gray-900">
|
45
|
+
<button type="submit" name="_saveasnew" class="border font-medium hidden px-3 py-2 rounded-md transition-all w-full hover:bg-gray-50 lg:block lg:w-auto dark:border-gray-700 dark:hover:text-gray-200 dark:hover:bg-gray-900">
|
46
46
|
{% translate 'Save as new' %}
|
47
47
|
</button>
|
48
48
|
{% endif %}
|
unfold/widgets.py
CHANGED
@@ -25,6 +25,7 @@ from django.forms import (
|
|
25
25
|
NumberInput,
|
26
26
|
PasswordInput,
|
27
27
|
Select,
|
28
|
+
SelectMultiple,
|
28
29
|
)
|
29
30
|
from django.utils.translation import gettext_lazy as _
|
30
31
|
|
@@ -480,7 +481,16 @@ class UnfoldAdminSelectWidget(Select):
|
|
480
481
|
if attrs is None:
|
481
482
|
attrs = {}
|
482
483
|
|
483
|
-
attrs["class"] = " ".join(SELECT_CLASSES)
|
484
|
+
attrs["class"] = " ".join([*SELECT_CLASSES, attrs.get("class", "")])
|
485
|
+
super().__init__(attrs, choices)
|
486
|
+
|
487
|
+
|
488
|
+
class UnfoldAdminSelectMultipleWidget(SelectMultiple):
|
489
|
+
def __init__(self, attrs=None, choices=()):
|
490
|
+
if attrs is None:
|
491
|
+
attrs = {}
|
492
|
+
|
493
|
+
attrs["class"] = " ".join([*SELECT_CLASSES, attrs.get("class", "")])
|
484
494
|
super().__init__(attrs, choices)
|
485
495
|
|
486
496
|
|
File without changes
|
File without changes
|