django-unfold 0.52.0__py3-none-any.whl → 0.54.0__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.
- {django_unfold-0.52.0.dist-info → django_unfold-0.54.0.dist-info}/METADATA +1 -1
- {django_unfold-0.52.0.dist-info → django_unfold-0.54.0.dist-info}/RECORD +42 -38
- {django_unfold-0.52.0.dist-info → django_unfold-0.54.0.dist-info}/WHEEL +1 -1
- unfold/contrib/filters/admin/__init__.py +17 -0
- unfold/contrib/filters/admin/choice_filters.py +134 -0
- unfold/contrib/filters/admin/dropdown_filters.py +3 -3
- unfold/contrib/filters/admin/mixins.py +1 -1
- unfold/contrib/filters/admin/text_filters.py +2 -2
- unfold/contrib/filters/forms.py +41 -3
- 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_numeric_slider.html +1 -1
- unfold/decorators.py +10 -4
- unfold/forms.py +2 -1
- unfold/paginator.py +10 -0
- unfold/sites.py +8 -6
- unfold/static/admin/js/inlines.js +23 -3
- unfold/static/unfold/css/styles.css +1 -1
- unfold/styles.css +2 -1
- unfold/templates/admin/auth/user/add_form.html +1 -1
- unfold/templates/admin/change_form.html +9 -8
- unfold/templates/admin/change_list.html +15 -13
- unfold/templates/admin/change_list_results.html +1 -1
- unfold/templates/admin/edit_inline/tabular.html +1 -2
- unfold/templates/admin/pagination.html +4 -20
- unfold/templates/unfold/components/chart/cohort.html +2 -2
- unfold/templates/unfold/helpers/change_list_filter.html +2 -2
- unfold/templates/unfold/helpers/change_list_filter_actions.html +28 -26
- unfold/templates/unfold/helpers/edit_inline/tabular_field.html +10 -8
- unfold/templates/unfold/helpers/field.html +4 -2
- unfold/templates/unfold/helpers/fieldset_row.html +19 -7
- unfold/templates/unfold/helpers/form_label.html +1 -1
- unfold/templates/unfold/helpers/pagination_default.html +23 -0
- unfold/templates/unfold/helpers/pagination_infinite.html +11 -0
- unfold/templates/unfold/helpers/site_icon.html +14 -14
- unfold/templates/unfold/helpers/tab_action.html +18 -3
- unfold/templates/unfold/helpers/welcomemsg.html +2 -2
- unfold/templates/unfold/widgets/radio_option.html +1 -1
- unfold/templatetags/unfold.py +66 -29
- unfold/utils.py +11 -0
- unfold/widgets.py +2 -0
- {django_unfold-0.52.0.dist-info → django_unfold-0.54.0.dist-info}/LICENSE.md +0 -0
unfold/templatetags/unfold.py
CHANGED
@@ -1,21 +1,24 @@
|
|
1
|
+
import json
|
1
2
|
from collections.abc import Mapping
|
2
3
|
from typing import Any, Optional, Union
|
3
4
|
|
4
5
|
from django import template
|
5
6
|
from django.contrib.admin.helpers import AdminForm, Fieldset
|
6
7
|
from django.contrib.admin.views.main import ChangeList
|
8
|
+
from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
|
7
9
|
from django.db.models import Model
|
8
10
|
from django.db.models.options import Options
|
9
|
-
from django.forms import Field
|
11
|
+
from django.forms import BoundField, Field
|
10
12
|
from django.http import HttpRequest
|
11
13
|
from django.template import Context, Library, Node, RequestContext, TemplateSyntaxError
|
12
14
|
from django.template.base import NodeList, Parser, Token, token_kwargs
|
13
15
|
from django.template.loader import render_to_string
|
14
|
-
from django.utils.safestring import SafeText
|
16
|
+
from django.utils.safestring import SafeText, mark_safe
|
15
17
|
|
16
18
|
from unfold.components import ComponentRegistry
|
17
19
|
from unfold.dataclasses import UnfoldAction
|
18
20
|
from unfold.enums import ActionVariant
|
21
|
+
from unfold.widgets import UnfoldAdminSplitDateTimeWidget
|
19
22
|
|
20
23
|
register = Library()
|
21
24
|
|
@@ -107,7 +110,10 @@ def class_name(value: Any) -> str:
|
|
107
110
|
|
108
111
|
@register.filter
|
109
112
|
def index(indexable: Mapping[int, Any], i: int) -> Any:
|
110
|
-
|
113
|
+
try:
|
114
|
+
return indexable[i]
|
115
|
+
except (KeyError, TypeError):
|
116
|
+
return None
|
111
117
|
|
112
118
|
|
113
119
|
@register.filter
|
@@ -346,7 +352,6 @@ def fieldset_row_classes(context: Context) -> str:
|
|
346
352
|
]
|
347
353
|
|
348
354
|
formset = context.get("inline_admin_formset", None)
|
349
|
-
adminform = context.get("adminform", None)
|
350
355
|
line = context.get("line")
|
351
356
|
|
352
357
|
# Hide the field in case of ordering field for sorting
|
@@ -359,28 +364,11 @@ def fieldset_row_classes(context: Context) -> str:
|
|
359
364
|
):
|
360
365
|
classes.append("hidden")
|
361
366
|
|
362
|
-
# Compressed fields special styling
|
363
|
-
if (
|
364
|
-
adminform
|
365
|
-
and hasattr(adminform.model_admin, "compressed_fields")
|
366
|
-
and adminform.model_admin.compressed_fields
|
367
|
-
):
|
368
|
-
classes.extend(
|
369
|
-
[
|
370
|
-
"lg:border-b",
|
371
|
-
"lg:border-base-200",
|
372
|
-
"lg:border-dashed",
|
373
|
-
"dark:lg:border-base-800",
|
374
|
-
"last:lg:border-b-0",
|
375
|
-
]
|
376
|
-
)
|
377
|
-
|
378
367
|
if len(line.fields) > 1:
|
379
368
|
classes.extend(
|
380
369
|
[
|
381
|
-
"
|
382
|
-
"
|
383
|
-
"flex-wrap",
|
370
|
+
"grid",
|
371
|
+
f"lg:grid-cols-{len(line.fields)}",
|
384
372
|
]
|
385
373
|
)
|
386
374
|
|
@@ -421,7 +409,6 @@ def fieldset_line_classes(context: Context) -> str:
|
|
421
409
|
"border-base-200",
|
422
410
|
"border-dashed",
|
423
411
|
"group-[.last]/row:border-b-0",
|
424
|
-
"lg:border-b-0",
|
425
412
|
"lg:border-l",
|
426
413
|
"lg:flex-row",
|
427
414
|
"dark:border-base-800",
|
@@ -451,41 +438,46 @@ def action_item_classes(context: Context, action: UnfoldAction) -> str:
|
|
451
438
|
if variant == ActionVariant.PRIMARY:
|
452
439
|
classes.extend(
|
453
440
|
[
|
454
|
-
"border-primary-
|
441
|
+
"border-primary-700",
|
455
442
|
"bg-primary-600",
|
456
443
|
"text-white",
|
444
|
+
"dark:border-primary-500",
|
457
445
|
]
|
458
446
|
)
|
459
447
|
elif variant == ActionVariant.DANGER:
|
460
448
|
classes.extend(
|
461
449
|
[
|
462
|
-
"border-red-
|
450
|
+
"border-red-700",
|
463
451
|
"bg-red-600",
|
464
452
|
"text-white",
|
453
|
+
"dark:border-red-500",
|
465
454
|
]
|
466
455
|
)
|
467
456
|
elif variant == ActionVariant.SUCCESS:
|
468
457
|
classes.extend(
|
469
458
|
[
|
470
|
-
"border-green-
|
459
|
+
"border-green-700",
|
471
460
|
"bg-green-600",
|
472
461
|
"text-white",
|
462
|
+
"dark:border-green-500",
|
473
463
|
]
|
474
464
|
)
|
475
465
|
elif variant == ActionVariant.INFO:
|
476
466
|
classes.extend(
|
477
467
|
[
|
478
|
-
"border-blue-
|
468
|
+
"border-blue-700",
|
479
469
|
"bg-blue-600",
|
480
470
|
"text-white",
|
471
|
+
"dark:border-blue-500",
|
481
472
|
]
|
482
473
|
)
|
483
474
|
elif variant == ActionVariant.WARNING:
|
484
475
|
classes.extend(
|
485
476
|
[
|
486
|
-
"border-orange-
|
477
|
+
"border-orange-700",
|
487
478
|
"bg-orange-600",
|
488
479
|
"text-white",
|
480
|
+
"dark:border-orange-500",
|
489
481
|
]
|
490
482
|
)
|
491
483
|
else:
|
@@ -493,8 +485,53 @@ def action_item_classes(context: Context, action: UnfoldAction) -> str:
|
|
493
485
|
[
|
494
486
|
"border-base-200",
|
495
487
|
"hover:text-primary-600",
|
488
|
+
"dark:hover:text-primary-500",
|
496
489
|
"dark:border-base-700",
|
497
490
|
]
|
498
491
|
)
|
499
492
|
|
500
493
|
return " ".join(set(classes))
|
494
|
+
|
495
|
+
|
496
|
+
@register.filter
|
497
|
+
def changeform_data(adminform: AdminForm) -> str:
|
498
|
+
fields = []
|
499
|
+
|
500
|
+
for fieldset in adminform:
|
501
|
+
for line in fieldset:
|
502
|
+
for field in line:
|
503
|
+
if isinstance(field.field, dict):
|
504
|
+
continue
|
505
|
+
|
506
|
+
if isinstance(field.field.field.widget, UnfoldAdminSplitDateTimeWidget):
|
507
|
+
for index, _widget in enumerate(field.field.field.widget.widgets):
|
508
|
+
fields.append(
|
509
|
+
f"{field.field.name}{field.field.field.widget.widgets_names[index]}"
|
510
|
+
)
|
511
|
+
else:
|
512
|
+
fields.append(field.field.name)
|
513
|
+
|
514
|
+
return mark_safe(json.dumps(dict.fromkeys(fields, "")))
|
515
|
+
|
516
|
+
|
517
|
+
@register.filter(takes_context=True)
|
518
|
+
def changeform_condition(field: BoundField) -> BoundField:
|
519
|
+
if isinstance(field.field, dict):
|
520
|
+
return field
|
521
|
+
|
522
|
+
if isinstance(field.field.field.widget, RelatedFieldWidgetWrapper):
|
523
|
+
field.field.field.widget.widget.attrs["x-model.fill"] = field.field.name
|
524
|
+
field.field.field.widget.widget.attrs["x-init"] = mark_safe(
|
525
|
+
f"const $ = django.jQuery; $(function () {{ const select = $('#{field.field.auto_id}'); select.on('change', (ev) => {{ {field.field.name} = select.val(); }}); }});"
|
526
|
+
)
|
527
|
+
elif isinstance(field.field.field.widget, UnfoldAdminSplitDateTimeWidget):
|
528
|
+
for index, widget in enumerate(field.field.field.widget.widgets):
|
529
|
+
field_name = (
|
530
|
+
f"{field.field.name}{field.field.field.widget.widgets_names[index]}"
|
531
|
+
)
|
532
|
+
|
533
|
+
widget.attrs["x-model.fill"] = field_name
|
534
|
+
else:
|
535
|
+
field.field.field.widget.attrs["x-model.fill"] = field.field.name
|
536
|
+
|
537
|
+
return field
|
unfold/utils.py
CHANGED
@@ -15,6 +15,13 @@ from django.utils.safestring import SafeText, mark_safe
|
|
15
15
|
|
16
16
|
from .exceptions import UnfoldException
|
17
17
|
|
18
|
+
try:
|
19
|
+
from djmoney.models.fields import MoneyField
|
20
|
+
from djmoney.money import Money
|
21
|
+
except ImportError:
|
22
|
+
MoneyField = None
|
23
|
+
Money = None
|
24
|
+
|
18
25
|
|
19
26
|
def _boolean_icon(field_val: Any) -> str:
|
20
27
|
return render_to_string("unfold/helpers/boolean.html", {"value": field_val})
|
@@ -89,6 +96,8 @@ def display_for_value(
|
|
89
96
|
return formats.localize(timezone.template_localtime(value))
|
90
97
|
elif isinstance(value, (datetime.date, datetime.time)):
|
91
98
|
return formats.localize(value)
|
99
|
+
elif Money is not None and isinstance(value, Money):
|
100
|
+
return str(value)
|
92
101
|
elif isinstance(value, (int, decimal.Decimal, float)):
|
93
102
|
return formats.number_format(value)
|
94
103
|
elif isinstance(value, (list, tuple)):
|
@@ -114,6 +123,8 @@ def display_for_field(value: Any, field: Any, empty_value_display: str) -> str:
|
|
114
123
|
return formats.localize(timezone.template_localtime(value))
|
115
124
|
elif isinstance(field, (models.DateField, models.TimeField)):
|
116
125
|
return formats.localize(value)
|
126
|
+
elif MoneyField is not None and isinstance(field, MoneyField):
|
127
|
+
return str(value)
|
117
128
|
elif isinstance(field, models.DecimalField):
|
118
129
|
return formats.number_format(value, field.decimal_places)
|
119
130
|
elif isinstance(field, (models.IntegerField, models.FloatField)):
|
unfold/widgets.py
CHANGED
@@ -146,6 +146,7 @@ CHECKBOX_CLASSES = [
|
|
146
146
|
"border-base-300",
|
147
147
|
"cursor-pointer",
|
148
148
|
"h-4",
|
149
|
+
"min-w-4",
|
149
150
|
"relative",
|
150
151
|
"rounded-[4px]",
|
151
152
|
"shadow-sm",
|
@@ -189,6 +190,7 @@ RADIO_CLASSES = [
|
|
189
190
|
"border-base-300",
|
190
191
|
"cursor-pointer",
|
191
192
|
"h-4",
|
193
|
+
"min-w-4",
|
192
194
|
"relative",
|
193
195
|
"rounded-full",
|
194
196
|
"w-4",
|
File without changes
|