django-unfold 0.56.0__py3-none-any.whl → 0.58.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.56.0.dist-info → django_unfold-0.58.0.dist-info}/METADATA +1 -1
- {django_unfold-0.56.0.dist-info → django_unfold-0.58.0.dist-info}/RECORD +45 -41
- unfold/contrib/import_export/templates/admin/import_export/base.html +1 -5
- unfold/contrib/import_export/templates/admin/import_export/import_form.html +1 -1
- unfold/contrib/simple_history/templates/simple_history/object_history_form.html +1 -1
- unfold/decorators.py +1 -2
- unfold/forms.py +3 -2
- unfold/layout.py +19 -1
- unfold/sites.py +64 -41
- unfold/static/unfold/css/styles.css +2 -2
- unfold/styles.css +66 -117
- unfold/templates/admin/actions.html +1 -1
- unfold/templates/admin/auth/user/change_password.html +1 -1
- unfold/templates/admin/submit_line.html +7 -7
- unfold/templates/registration/password_change_done.html +1 -1
- unfold/templates/registration/password_change_form.html +1 -1
- unfold/templates/unfold/components/card.html +23 -23
- unfold/templates/unfold/components/progress.html +9 -7
- unfold/templates/unfold/components/table.html +5 -5
- unfold/templates/unfold/helpers/change_list_filter_actions.html +1 -1
- unfold/templates/unfold/helpers/edit_inline/tabular_field.html +1 -1
- unfold/templates/unfold/helpers/field.html +2 -2
- unfold/templates/unfold/helpers/field_readonly_value.html +1 -1
- unfold/templates/unfold/helpers/fieldset_row_checkbox.html +2 -2
- unfold/templates/unfold/helpers/fieldset_row_field.html +2 -2
- unfold/templates/unfold/helpers/form_errors.html +1 -1
- unfold/templates/unfold/helpers/messages/errornote.html +1 -5
- unfold/templates/unfold/helpers/navigation.html +1 -1
- unfold/templates/unfold/layouts/skeleton.html +1 -1
- unfold/templates/unfold/widgets/clearable_file_input.html +3 -3
- unfold/templates/unfold/widgets/clearable_file_input_small.html +1 -1
- unfold/templates/unfold/widgets/select.html +9 -0
- unfold/templates/unfold_crispy/errors_formset.html +3 -0
- unfold/templates/unfold_crispy/field.html +1 -1
- unfold/templates/unfold_crispy/inputs.html +1 -1
- unfold/templates/unfold_crispy/layout/checkbox.html +2 -2
- unfold/templates/unfold_crispy/layout/field_errors.html +5 -5
- unfold/templates/unfold_crispy/layout/fieldset.html +1 -1
- unfold/templates/unfold_crispy/layout/hr.html +13 -0
- unfold/templates/unfold_crispy/layout/table_inline_formset.html +23 -19
- unfold/templates/unfold_crispy/whole_uni_formset.html +30 -0
- unfold/templatetags/unfold.py +14 -0
- unfold/widgets.py +32 -2
- {django_unfold-0.56.0.dist-info → django_unfold-0.58.0.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.56.0.dist-info → django_unfold-0.58.0.dist-info}/WHEEL +0 -0
@@ -58,7 +58,7 @@
|
|
58
58
|
{% endblock %}
|
59
59
|
</head>
|
60
60
|
|
61
|
-
<body class="antialiased bg-base-50 font-sans text-font-default-light text-sm dark:bg-base-900 dark:text-font-default-dark {% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}" data-admin-utc-offset="{% now "Z" %}" x-data="{{% if opts %}activeTab: 'general',{% endif %} sidebarMobileOpen: false, sidebarDesktopOpen: {% if request.session.toggle_sidebar == False %}false{% else %}true{% endif %} }" x-init="activeTab = {% if opts %}window.location.hash?.replace('#', '') || 'general'{% else %}''{% endif %}">
|
61
|
+
<body class="antialiased bg-base-50 font-sans text-font-default-light text-sm dark:bg-base-900 dark:text-font-default-dark {% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}" data-admin-utc-offset="{% now "Z" %}" x-data="{ {% if opts %}activeTab: 'general',{% endif %} sidebarMobileOpen: false, sidebarDesktopOpen: {% if request.session.toggle_sidebar == False %}false{% else %}true{% endif %} }" x-init="activeTab = {% if opts %}window.location.hash?.replace('#', '') || 'general'{% else %}''{% endif %}">
|
62
62
|
{% if colors %}
|
63
63
|
<style id="unfold-theme-colors">
|
64
64
|
:root {
|
@@ -24,18 +24,18 @@
|
|
24
24
|
<input type="text" aria-label="{% trans 'Choose file to upload' %}" value="{% if widget.value %}{{ widget.value.url }}{% else %}{% trans 'Choose file to upload' %}{% endif %}" disabled class="bg-white grow font-medium min-w-0 px-3 py-2 text-ellipsis dark:bg-base-900">
|
25
25
|
|
26
26
|
<div class="bg-white flex flex-none items-center leading-none self-stretch dark:bg-base-900">
|
27
|
-
<div class="opacity-0 w-[0px]">
|
27
|
+
<div class="opacity-0 overflow-hidden w-[0px]">
|
28
28
|
<input type="{{ widget.type }}" name="{{ widget.name }}" {% include "django/forms/widgets/attrs.html" %} />
|
29
29
|
</div>
|
30
30
|
|
31
31
|
{% if widget.is_initial %}
|
32
32
|
<a href="{{ widget.value.url }}" class="border-r border-base-200 cursor-pointer text-base-400 px-3 hover:text-base-700 dark:border-base-700 dark:text-base-500 dark:hover:text-base-200" target="_blank">
|
33
|
-
<span class="material-symbols-outlined">download</span>
|
33
|
+
<span class="block material-symbols-outlined">download</span>
|
34
34
|
</a>
|
35
35
|
{% endif %}
|
36
36
|
|
37
37
|
<label for="{{ widget.attrs.id }}" class="cursor-pointer text-base-400 px-3 hover:text-base-700 dark:text-base-500 dark:hover:text-base-200">
|
38
|
-
<span class="material-symbols-outlined">upload</span>
|
38
|
+
<span class="block material-symbols-outlined">upload</span>
|
39
39
|
</label>
|
40
40
|
</div>
|
41
41
|
</div>
|
@@ -17,7 +17,7 @@
|
|
17
17
|
<input type="text" aria-label="{% trans 'Choose file to upload' %}" value="{% if widget.value %}{{ widget.value.url }}{% else %}{% trans 'Choose file to upload' %}{% endif %}" disabled class="bg-white grow font-medium min-w-0 px-3 py-2 text-ellipsis dark:bg-base-900">
|
18
18
|
|
19
19
|
<div class="flex flex-none items-center leading-none self-stretch">
|
20
|
-
<div class="opacity-0 w-[0px]">
|
20
|
+
<div class="opacity-0 overflow-hidden w-[0px]">
|
21
21
|
<input type="{{ widget.type }}" name="{{ widget.name }}" {% include "django/forms/widgets/attrs.html" %} />
|
22
22
|
</div>
|
23
23
|
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<div class="grow relative max-w-2xl">
|
2
|
+
<select name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>{% for group_name, group_choices, group_index in widget.optgroups %}{% if group_name %}
|
3
|
+
<optgroup label="{{ group_name }}">{% endif %}{% for option in group_choices %}
|
4
|
+
{% include option.template_name with widget=option %}{% endfor %}{% if group_name %}
|
5
|
+
</optgroup>{% endif %}{% endfor %}
|
6
|
+
</select>
|
7
|
+
|
8
|
+
<span class="material-symbols-outlined absolute group-[.primary]:text-white -ml-[31px] pointer-events-none text-base-400 top-0 top-1/2 hover:text-base-700 dark:text-base-500 dark:hover:text-base-200 -translate-y-1/2">expand_more</span>
|
9
|
+
</div>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
{% if field.is_hidden %}
|
4
4
|
{{ field }}
|
5
5
|
{% else %}
|
6
|
-
<{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="group {% if tag == "td" %}align-top border-t border-base-200 font-normal gap-4 min-w-0 overflow-hidden px-3 py-3 text-left dark:border-base-800 dark:before:text-font-important-dark{% endif%} {% if field.errors %}errors{% endif %} {% if field_class %} {{ field_class }}{% endif %} {% if field|is_checkbox and tag == "td" %}flex flex-row gap-2
|
6
|
+
<{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="group {% if tag == "td" %}align-top border-t border-base-200 font-normal gap-4 min-w-0 overflow-hidden px-3 py-3 text-left dark:border-base-800 dark:before:text-font-important-dark {% if form_show_labels and forloop.parentloop.first %}border-t-0{% endif %}{% endif %} {% if field.errors %}errors{% endif %} {% if field_class %} {{ field_class }}{% endif %} {% if field|is_checkbox and tag == "td" %}flex flex-row gap-2 items-center{% else %}{% if 'form-horizontal' in form_class %} row{% endif %}{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
|
7
7
|
{% if field.label and not field|is_checkbox and form_show_labels %}
|
8
8
|
<label {% if field.id_for_label %}for="{{ field.id_for_label }}"{% endif %} class="block font-semibold mb-2 text-font-important-light text-sm dark:text-font-important-dark {% if label_class %} {{ label_class }}{% endif %}">
|
9
9
|
{{ field.label }}{% if field.field.required %} <span class="asteriskField">*</span>{% endif %}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
{% if inputs %}
|
2
|
-
<div class="
|
2
|
+
<div class="flex flex-col gap-2 justify-end lg:flex-row {{ field_class }}">
|
3
3
|
{% for input in inputs %}
|
4
4
|
{% include "unfold_crispy/layout/baseinput.html" %}
|
5
5
|
{% endfor %}
|
@@ -1,13 +1,13 @@
|
|
1
1
|
{% load crispy_forms_field unfold %}
|
2
2
|
|
3
3
|
{% if form_show_labels %}
|
4
|
-
<label for="{{ field.id_for_label }}" class="flex flex-row gap-3 items-center">
|
4
|
+
<label for="{{ field.id_for_label }}" class="flex flex-row gap-3 items-center {% if tag == "td" %}mt-9{% endif %}">
|
5
5
|
{% crispy_field field 'class' form_classes.switch %} <span>{{ field.label }}{% if field.field.required %} <span class="asteriskField">*</span>{% endif %}</span>
|
6
6
|
</label>
|
7
7
|
|
8
8
|
{% include 'unfold_crispy/layout/help_text_and_errors.html' %}
|
9
9
|
{% else %}
|
10
|
-
{% with field=field|add_css_class:form_classes.
|
10
|
+
{% with field=field|add_css_class:form_classes.switch %}
|
11
11
|
{% if tag == "td" %}
|
12
12
|
<div class="flex items-center justify-center h-[38px]">
|
13
13
|
{% crispy_field field %}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{% if form_show_errors and field.errors %}
|
2
|
-
|
3
|
-
|
4
|
-
{{ error }}
|
5
|
-
|
6
|
-
|
2
|
+
<ul class="errorlist">
|
3
|
+
{% for error in field.errors %}
|
4
|
+
<li id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="mt-2 text-red-600 text-xs dark:text-red-500">{{ error }}</li>
|
5
|
+
{% endfor %}
|
6
|
+
</ul>
|
7
7
|
{% endif %}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<fieldset {% if fieldset.css_id %}id="{{ fieldset.css_id }}"{% endif %} class="fieldset group flex flex-col gap-5 grow rounded-default border-base-200 shadow-xs aligned border p-3 relative dark:border-base-800 {% if fieldset.css_class %} {{ fieldset.css_class }}{% endif %}" {{ fieldset.flat_attrs }}>
|
2
2
|
{% if legend %}
|
3
|
-
<legend class="border-b border-base-200 font-semibold float-left pb-3 -mx-3 px-3 text-font-important-light dark:text-font-important-dark dark:border-base-800">
|
3
|
+
<legend class="border-b border-base-200 font-semibold float-left pb-3 -mx-3 px-3 text-[15px] text-font-important-light dark:text-font-important-dark dark:border-base-800">
|
4
4
|
{{ legend|safe }}
|
5
5
|
</legend>
|
6
6
|
{% endif %}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<div class="flex flex-row justify-center items-center gap-4 my-4">
|
2
|
+
{% if title %}
|
3
|
+
<span class="bg-base-200 grow h-px"></span>
|
4
|
+
|
5
|
+
{% if title %}
|
6
|
+
<span>
|
7
|
+
{{ title }}
|
8
|
+
</span>
|
9
|
+
{% endif %}
|
10
|
+
{% endif %}
|
11
|
+
|
12
|
+
<span class="bg-base-200 grow h-px"></span>
|
13
|
+
</div>
|
@@ -9,24 +9,28 @@
|
|
9
9
|
{% csrf_token %}
|
10
10
|
{% endif %}
|
11
11
|
|
12
|
-
|
12
|
+
{% include "unfold_crispy/errors_formset.html" %}
|
13
|
+
|
14
|
+
<div class="overflow-x-auto border border-base-200 mb-8 rounded-default shadow-xs dark:border-base-800" {% if form_id %} id="{{ form_id }}"{% endif %}>
|
13
15
|
{{ formset.management_form|crispy }}
|
14
16
|
|
15
17
|
<table class="w-full">
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
{%
|
22
|
-
|
23
|
-
{{ field.
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
18
|
+
{% if not form_show_labels %}
|
19
|
+
<thead>
|
20
|
+
{% if formset.readonly and not formset.queryset.exists %}
|
21
|
+
{% else %}
|
22
|
+
<tr>
|
23
|
+
{% for field in formset.forms.0 %}
|
24
|
+
{% if field.label and not field.is_hidden %}
|
25
|
+
<th for="{{ field.auto_id }}" class="align-middle font-semibold py-2 text-left text-font-important-light dark:text-font-important-dark whitespace-nowrap px-3 {% if field.name == "DELETE" %}w-0{% endif %}">
|
26
|
+
{{ field.label }}{% if field.field.required and not field|is_checkbox %} <span class="asteriskField">*</span>{% endif %}
|
27
|
+
</th>
|
28
|
+
{% endif %}
|
29
|
+
{% endfor %}
|
30
|
+
</tr>
|
31
|
+
{% endif %}
|
32
|
+
</thead>
|
33
|
+
{% endif %}
|
30
34
|
|
31
35
|
<tbody {% if formset_id %}id="{{ formset_id }}-rows"{% endif %}>
|
32
36
|
{% for form in formset %}
|
@@ -40,7 +44,7 @@
|
|
40
44
|
|
41
45
|
<tr>
|
42
46
|
{% for field in form %}
|
43
|
-
{% include 'unfold_crispy/field.html' with tag="td"
|
47
|
+
{% include 'unfold_crispy/field.html' with tag="td" %}
|
44
48
|
{% endfor %}
|
45
49
|
</tr>
|
46
50
|
{% endfor %}
|
@@ -50,7 +54,7 @@
|
|
50
54
|
<tbody>
|
51
55
|
<tr class="empty-form">
|
52
56
|
{% for field in formset.empty_form %}
|
53
|
-
{% include 'unfold_crispy/field.html' with tag="td"
|
57
|
+
{% include 'unfold_crispy/field.html' with tag="td" %}
|
54
58
|
{% endfor %}
|
55
59
|
</tr>
|
56
60
|
|
@@ -66,10 +70,10 @@
|
|
66
70
|
</tbody>
|
67
71
|
{% endif %}
|
68
72
|
</table>
|
69
|
-
|
70
|
-
{% include "unfold_crispy/inputs.html" %}
|
71
73
|
</div>
|
72
74
|
|
75
|
+
{% include "unfold_crispy/inputs.html" %}
|
76
|
+
|
73
77
|
{% if formset_tag %}
|
74
78
|
</form>
|
75
79
|
{% endif %}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
{% load crispy_forms_tags %}
|
2
|
+
{% load crispy_forms_utils %}
|
3
|
+
|
4
|
+
{% specialspaceless %}
|
5
|
+
{% if formset_tag %}
|
6
|
+
<form {{ flat_attrs }} method="{{ form_method }}" {% if formset.is_multipart %} enctype="multipart/form-data"{% endif %}>
|
7
|
+
{% endif %}
|
8
|
+
{% if formset_method|lower == 'post' and not disable_csrf %}
|
9
|
+
{% csrf_token %}
|
10
|
+
{% endif %}
|
11
|
+
|
12
|
+
<div>
|
13
|
+
{{ formset.management_form|crispy }}
|
14
|
+
</div>
|
15
|
+
|
16
|
+
{% include "unfold_crispy/errors_formset.html" %}
|
17
|
+
|
18
|
+
{% for form in formset %}
|
19
|
+
{% include "unfold_crispy/display_form.html" %}
|
20
|
+
{% endfor %}
|
21
|
+
|
22
|
+
{% if inputs %}
|
23
|
+
<div class="form-actions">
|
24
|
+
{% for input in inputs %}
|
25
|
+
{% include "unfold_crispy/layout/baseinput.html" %}
|
26
|
+
{% endfor %}
|
27
|
+
</div>
|
28
|
+
{% endif %}
|
29
|
+
{% if formset_tag %}</form>{% endif %}
|
30
|
+
{% endspecialspaceless %}
|
unfold/templatetags/unfold.py
CHANGED
@@ -108,6 +108,20 @@ def class_name(value: Any) -> str:
|
|
108
108
|
return value.__class__.__name__
|
109
109
|
|
110
110
|
|
111
|
+
@register.filter
|
112
|
+
def is_list(value: Any) -> str:
|
113
|
+
return isinstance(value, list)
|
114
|
+
|
115
|
+
|
116
|
+
@register.filter
|
117
|
+
def has_active_item(items: list[dict]) -> bool:
|
118
|
+
for item in items:
|
119
|
+
if "active" in item and item["active"]:
|
120
|
+
return True
|
121
|
+
|
122
|
+
return False
|
123
|
+
|
124
|
+
|
111
125
|
@register.filter
|
112
126
|
def index(indexable: Mapping[int, Any], i: int) -> Any:
|
113
127
|
try:
|
unfold/widgets.py
CHANGED
@@ -86,6 +86,7 @@ BASE_CLASSES = [
|
|
86
86
|
"dark:group-[.errors]:border-red-500",
|
87
87
|
"dark:focus:group-[.errors]:outline-red-500",
|
88
88
|
"dark:scheme-dark",
|
89
|
+
"group-[.primary]:border-transparent",
|
89
90
|
]
|
90
91
|
|
91
92
|
BASE_INPUT_CLASSES = [
|
@@ -167,7 +168,7 @@ CHECKBOX_CLASSES = [
|
|
167
168
|
"dark:border-base-500",
|
168
169
|
"dark:checked:after:text-white",
|
169
170
|
"focus:outline",
|
170
|
-
"focus:outline-
|
171
|
+
"focus:outline-2",
|
171
172
|
"focus:outline-offset-2",
|
172
173
|
"focus:outline-primary-500",
|
173
174
|
"after:absolute",
|
@@ -209,7 +210,7 @@ RADIO_CLASSES = [
|
|
209
210
|
"dark:border-base-500",
|
210
211
|
"hover:border-base-400",
|
211
212
|
"focus:outline",
|
212
|
-
"focus:outline-
|
213
|
+
"focus:outline-2",
|
213
214
|
"focus:outline-offset-2",
|
214
215
|
"focus:outline-primary-500",
|
215
216
|
"after:absolute",
|
@@ -633,6 +634,8 @@ class UnfoldAdminBigIntegerFieldWidget(AdminBigIntegerFieldWidget):
|
|
633
634
|
|
634
635
|
|
635
636
|
class UnfoldAdminNullBooleanSelectWidget(NullBooleanSelect):
|
637
|
+
template_name = "unfold/widgets/select.html"
|
638
|
+
|
636
639
|
def __init__(self, attrs=None):
|
637
640
|
if attrs is None:
|
638
641
|
attrs = {}
|
@@ -644,6 +647,8 @@ class UnfoldAdminNullBooleanSelectWidget(NullBooleanSelect):
|
|
644
647
|
|
645
648
|
|
646
649
|
class UnfoldAdminSelectWidget(Select):
|
650
|
+
template_name = "unfold/widgets/select.html"
|
651
|
+
|
647
652
|
def __init__(self, attrs=None, choices=()):
|
648
653
|
if attrs is None:
|
649
654
|
attrs = {}
|
@@ -690,6 +695,31 @@ class UnfoldAdminSelectMultipleWidget(SelectMultiple):
|
|
690
695
|
super().__init__(attrs, choices)
|
691
696
|
|
692
697
|
|
698
|
+
class UnfoldAdminSelect2MultipleWidget(SelectMultiple):
|
699
|
+
def __init__(self, attrs=None, choices=()):
|
700
|
+
if attrs is None:
|
701
|
+
attrs = {}
|
702
|
+
|
703
|
+
attrs["data-theme"] = "admin-autocomplete"
|
704
|
+
attrs["class"] = "unfold-admin-autocomplete admin-autocomplete"
|
705
|
+
|
706
|
+
super().__init__(attrs, choices)
|
707
|
+
|
708
|
+
class Media:
|
709
|
+
js = (
|
710
|
+
"admin/js/vendor/jquery/jquery.js",
|
711
|
+
"admin/js/vendor/select2/select2.full.js",
|
712
|
+
"admin/js/jquery.init.js",
|
713
|
+
"unfold/js/select2.init.js",
|
714
|
+
)
|
715
|
+
css = {
|
716
|
+
"screen": (
|
717
|
+
"admin/css/vendor/select2/select2.css",
|
718
|
+
"admin/css/autocomplete.css",
|
719
|
+
),
|
720
|
+
}
|
721
|
+
|
722
|
+
|
693
723
|
class UnfoldAdminRadioSelectWidget(AdminRadioSelect):
|
694
724
|
template_name = "unfold/widgets/radio.html"
|
695
725
|
option_template_name = "unfold/widgets/radio_option.html"
|
File without changes
|
File without changes
|