django-unfold 0.21.1__py3-none-any.whl → 0.23.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. {django_unfold-0.21.1.dist-info → django_unfold-0.23.0.dist-info}/LICENSE.md +1 -1
  2. {django_unfold-0.21.1.dist-info → django_unfold-0.23.0.dist-info}/METADATA +26 -2
  3. {django_unfold-0.21.1.dist-info → django_unfold-0.23.0.dist-info}/RECORD +38 -38
  4. unfold/admin.py +29 -22
  5. unfold/apps.py +5 -0
  6. unfold/checks.py +1 -1
  7. unfold/contrib/filters/apps.py +1 -0
  8. unfold/contrib/forms/apps.py +1 -0
  9. unfold/contrib/guardian/apps.py +1 -1
  10. unfold/contrib/import_export/apps.py +1 -1
  11. unfold/forms.py +6 -1
  12. unfold/sites.py +4 -4
  13. unfold/static/unfold/css/styles.css +1 -1
  14. unfold/styles.css +15 -4
  15. unfold/templates/admin/actions.html +9 -9
  16. unfold/templates/admin/app_list.html +6 -14
  17. unfold/templates/admin/base.html +2 -4
  18. unfold/templates/admin/change_form.html +1 -2
  19. unfold/templates/admin/change_list.html +3 -3
  20. unfold/templates/admin/change_list_results.html +17 -5
  21. unfold/templates/admin/edit_inline/tabular.html +3 -3
  22. unfold/templates/admin/login.html +9 -5
  23. unfold/templates/admin/search_form.html +10 -12
  24. unfold/templates/unfold/helpers/account_links.html +2 -2
  25. unfold/templates/unfold/helpers/app_list.html +5 -1
  26. unfold/templates/unfold/helpers/display_label.html +2 -0
  27. unfold/templates/unfold/helpers/tab_list.html +1 -1
  28. unfold/templates/unfold/layouts/base_simple.html +1 -1
  29. unfold/templates/unfold/widgets/date.html +1 -1
  30. unfold/templates/{admin → unfold}/widgets/related_widget_wrapper.html +4 -4
  31. unfold/templates/unfold/widgets/time.html +1 -1
  32. unfold/templatetags/unfold_list.py +63 -48
  33. unfold/widgets.py +16 -3
  34. {django_unfold-0.21.1.dist-info → django_unfold-0.23.0.dist-info}/WHEEL +0 -0
  35. /unfold/templates/{admin → unfold}/widgets/clearable_file_input.html +0 -0
  36. /unfold/templates/{admin → unfold}/widgets/radio.html +0 -0
  37. /unfold/templates/{admin → unfold}/widgets/radio_option.html +0 -0
  38. /unfold/templates/{admin → unfold}/widgets/split_datetime.html +0 -0
@@ -65,7 +65,7 @@
65
65
  {% csrf_token %}
66
66
 
67
67
  {% if actions_detail %}
68
- <div class="bg-gray-50 flex justify-end -mt-6 mb-4 p-3 rounded-md dark:bg-gray-800">
68
+ <div class="bg-gray-50 flex justify-end mb-4 p-3 rounded-md dark:bg-gray-800">
69
69
  {% for action in actions_detail %}
70
70
  <a href="{{ action.path }}" class="bg-white text-gray-500 border cursor-pointer flex font-medium items-center px-3 py-2 mr-3 rounded-md shadow-sm text-sm dark:bg-gray-900 dark:border dark:border-gray-700 dark:text-gray-400"
71
71
  {% for attr_name, attr_value in action.attrs.items %}
@@ -89,7 +89,6 @@
89
89
  <input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">
90
90
  {% endif %}
91
91
 
92
-
93
92
  {% include "unfold/helpers/messages/errornote.html" with errors=errors %}
94
93
  {% include "unfold/helpers/messages/error.html" with errors=adminform.form.non_field_errors %}
95
94
 
@@ -67,7 +67,7 @@
67
67
  {% endif %}
68
68
 
69
69
  <div class="flex -mx-4 module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
70
- <div class="changelist-form-container flex-grow px-4" x-data="{ filterOpen: false }">
70
+ <div class="changelist-form-container flex-grow min-w-0 px-4" x-data="{ filterOpen: false }">
71
71
  {% block date_hierarchy %}
72
72
  {% if cl.date_hierarchy %}
73
73
  {% date_hierarchy cl %}
@@ -84,7 +84,7 @@
84
84
  {% block result_list %}
85
85
  {% if actions_on_top %}
86
86
  {% if cl.search_fields or action_form or cl.has_filters %}
87
- <div class="bg-gray-50 flex flex-col gap-3 mb-4 p-3 rounded-md md:flex-row dark:bg-gray-800">
87
+ <div class="bg-gray-50 flex flex-col gap-3 mb-4 p-3 rounded-md lg:flex-row dark:bg-gray-800">
88
88
  {% block search %}
89
89
  {% search_form cl %}
90
90
  {% endblock %}
@@ -95,7 +95,7 @@
95
95
 
96
96
  {% block filters %}
97
97
  {% if cl.has_filters %}
98
- <a class="{% if cl.has_active_filters %}bg-primary-600 border-primary-600 text-white{% else %}bg-white text-gray-500 hover:text-gray-700 dark:bg-gray-900 dark:border-gray-700 dark:hover:text-gray-200 dark:text-gray-400{% endif %} border cursor-pointer flex font-medium group items-center px-3 py-2 rounded-md shadow-sm text-sm md:ml-auto md:mt-0" x-on:click="filterOpen = true" x-on:keydown.escape.window="filterOpen = false">
98
+ <a class="{% if cl.has_active_filters %}bg-primary-600 border-primary-600 text-white{% else %}bg-white text-gray-500 hover:text-gray-700 dark:bg-gray-900 dark:border-gray-700 dark:hover:text-gray-200 dark:text-gray-400{% endif %} border cursor-pointer flex font-medium group items-center px-3 py-2 rounded-md shadow-sm text-sm lg:ml-auto md:mt-0" x-on:click="filterOpen = true" x-on:keydown.escape.window="filterOpen = false">
99
99
  {% trans "Filters" %}
100
100
 
101
101
  <span class="material-symbols-outlined md-18 ml-auto -mr-1 pl-4 {% if cl.has_active_filters %}text-white{% else %}text-gray-400 group-hover:text-gray-700 dark:group-hover:text-gray-200 dark:text-gray-500{% endif %}">filter_list</span>
@@ -7,11 +7,11 @@
7
7
  {% endif %}
8
8
 
9
9
  {% if results %}
10
- <table id="result_list" class="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:dark:border-gray-800">
11
- <thead class="hidden lg:table-header-group">
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>
12
12
  <tr>
13
13
  {% for header in result_headers %}
14
- <th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm {{ header.class_attrib }} {% if "action-toggle" in header.text and forloop.counter == 1 %}w-10{% endif %}" scope="col">
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
- <span>{{ header.text|capfirst }}</span>
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
 
@@ -52,7 +64,7 @@
52
64
  </tr>
53
65
  </thead>
54
66
 
55
- <tbody>
67
+ <tbody class="block lg:table-row-group">
56
68
  {% for result in results %}
57
69
  {% if result.form and result.form.non_field_errors %}
58
70
  <tr>
@@ -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
- <p 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">
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
- </p>
116
+ </div>
117
117
  {% else %}
118
118
  {{ field.field }}
119
119
 
@@ -25,7 +25,7 @@
25
25
 
26
26
  {% block base %}
27
27
  <div class="flex min-h-screen">
28
- <div class="flex flex-grow items-center justify-center mx-auto px-4">
28
+ <div class="flex flex-grow items-center justify-center mx-auto px-4 relative">
29
29
  <div class="w-full sm:w-96">
30
30
  <h1 class="font-semibold mb-10">
31
31
  <span class="block text-gray-700 dark:text-gray-200">{% trans 'Welcome back to' %}</span>
@@ -72,13 +72,17 @@
72
72
  </form>
73
73
  </div>
74
74
 
75
- {% if site_url %}
76
- <div class="absolute left-0 ml-4 mt-4 top-0">
75
+ <div class="absolute flex flex-row items-center justify-between left-0 m-4 right-0 top-0">
76
+ {% if site_url %}
77
77
  <a href="{{ site_url }}" class="flex font-medium items-center text-sm text-primary-600">
78
78
  <span class="material-symbols-outlined mr-2">arrow_back</span> {% trans 'Return to site' %}
79
79
  </a>
80
- </div>
81
- {% endif %}
80
+ {% endif %}
81
+
82
+ {% if not theme %}
83
+ {% include "unfold/helpers/theme_switch.html" %}
84
+ {% endif %}
85
+ </div>
82
86
  </div>
83
87
 
84
88
  {% if image %}
@@ -1,19 +1,17 @@
1
1
  {% load i18n static %}
2
2
 
3
3
  {% if cl.search_fields %}
4
- <div id="toolbar" class="lg:w-72">
5
- <div>
6
- <div class="bg-white border flex rounded-md overflow-hidden shadow-sm 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">
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
- <button type="submit" class="flex items-center ml-auto px-2 focus:outline-none" id="searchbar-submit">
10
- <span class="material-symbols-outlined md-18 text-gray-400 dark:text-gray-500">search</span>
11
- </button>
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 %}
@@ -25,10 +25,10 @@
25
25
  {% endif %}
26
26
 
27
27
  <div class="border-t mt-1 pt-1 dark:border-gray-700">
28
- <form id="logout-form" method="post" action="{% url 'admin:logout' %}" class="bg-none block mx-1 px-3 py-2 text-red-500 rounded hover:bg-red-100 dark:hover:bg-red-500/20 dark:hover:text-red-500">
28
+ <form id="logout-form" method="post" action="{% url 'admin:logout' %}" class="mx-1">
29
29
  {% csrf_token %}
30
30
 
31
- <button type="submit">
31
+ <button type="submit" class="bg-none block px-3 py-2 text-left text-red-500 rounded w-full hover:bg-red-100 dark:hover:bg-red-500/20 dark:hover:text-red-500">
32
32
  {% translate 'Log out' %}
33
33
  </button>
34
34
  </form>
@@ -31,7 +31,11 @@
31
31
 
32
32
  {% if item.badge %}
33
33
  <span class="bg-red-600 font-semibold ml-2 px-1 rounded-sm text-xs text-white">
34
- {{ item.badge|safe }}
34
+ {% if item.badge_callback %}
35
+ {{ item.badge_callback }}
36
+ {% else %}
37
+ {{ item.badge }}
38
+ {% endif %}
35
39
  </span>
36
40
  {% endif %}
37
41
  </a>
@@ -6,4 +6,6 @@
6
6
  {% else %}
7
7
  {% include "unfold/helpers/label.html" with type=label_type text=label %}
8
8
  {% endif %}
9
+ {% else %}
10
+ -
9
11
  {% endif %}
@@ -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-2 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">
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 %}
@@ -10,7 +10,7 @@
10
10
  {% endblock %}
11
11
  {% endif %}
12
12
 
13
- <div id="main" class="shadow flex-grow">
13
+ <div id="main" class="shadow flex-grow min-w-0">
14
14
  {% block content_before %}
15
15
  {% include "unfold/helpers/header.html" %}
16
16
  {% endblock %}
@@ -1,3 +1,3 @@
1
- <div class="flex flex-col max-w-2xl relative">
1
+ <div class="flex flex-col max-w-2xl min-w-48 relative w-full">
2
2
  {% include "django/forms/widgets/input.html" %}
3
3
  </div>
@@ -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 transition-colors w-9.5 hover:text-gray-700 dark:border-gray-700 dark:text-gray-500 dark:hover:text-gray-200"
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 transition-colors w-9.5 hover:text-gray-700 dark:border-gray-700 dark:text-gray-500 dark:hover:text-gray-200"
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 transition-colors w-9.5 hover:text-gray-700 dark:border-gray-700 dark:text-gray-500 dark:hover:text-gray-200"
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 transition-colors w-9.5 dark:border-gray-700 dark:text-red-500"
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"
@@ -1,3 +1,3 @@
1
- <div class="flex flex-col max-w-2xl relative">
1
+ <div class="flex flex-col max-w-2xl min-w-48 relative w-full">
2
2
  {% include "django/forms/widgets/input.html" %}
3
3
  </div>
@@ -36,7 +36,63 @@ from ..widgets import UnfoldBooleanWidget
36
36
 
37
37
  register = Library()
38
38
 
39
- LINK_CLASSES = ["text-gray-700 dark:text-gray-200"]
39
+ LINK_CLASSES = [
40
+ "text-gray-600",
41
+ "truncate",
42
+ "dark:text-gray-300",
43
+ ]
44
+
45
+ ROW_CLASSES = [
46
+ "align-middle",
47
+ "flex",
48
+ "border-t",
49
+ "border-gray-200",
50
+ "font-normal",
51
+ "gap-4",
52
+ "min-w-0",
53
+ "overflow-hidden",
54
+ "px-3",
55
+ "py-2",
56
+ "text-left",
57
+ "text-gray-500",
58
+ "text-sm",
59
+ "before:flex",
60
+ "before:capitalize",
61
+ "before:content-[attr(data-label)]",
62
+ "before:items-center",
63
+ "before:mr-auto",
64
+ "before:text-gray-500",
65
+ "first:border-t-0",
66
+ "dark:before:text-gray-400",
67
+ "dark:text-gray-400",
68
+ "lg:before:hidden",
69
+ "lg:first:border-t",
70
+ "lg:py-3",
71
+ "lg:table-cell",
72
+ "dark:border-gray-800",
73
+ ]
74
+
75
+ CHECKBOX_CLASSES = [
76
+ "action-checkbox",
77
+ "align-middle",
78
+ "flex",
79
+ "items-center",
80
+ "px-3",
81
+ "py-2",
82
+ "text-left",
83
+ "text-sm",
84
+ "before:block",
85
+ "before:capitalize",
86
+ "before:content-[attr(data-label)]",
87
+ "before:mr-auto",
88
+ "before:text-gray-500",
89
+ "lg:before:hidden",
90
+ "lg:border-t",
91
+ "lg:border-gray-200",
92
+ "lg:table-cell",
93
+ "dark:before:text-gray-400",
94
+ "dark:lg:border-gray-800",
95
+ ]
40
96
 
41
97
 
42
98
  def result_headers(cl):
@@ -95,7 +151,7 @@ def result_headers(cl):
95
151
  if is_sorted:
96
152
  order_type = ordering_field_columns.get(i).lower()
97
153
  sort_priority = list(ordering_field_columns).index(i) + 1
98
- th_classes.append("sorted %sending" % order_type)
154
+ th_classes.append(f"sorted {order_type}ending")
99
155
  new_order_type = {"asc": "desc", "desc": "asc"}[order_type]
100
156
 
101
157
  # build new ordering param
@@ -132,7 +188,7 @@ def result_headers(cl):
132
188
  "url_primary": cl.get_query_string({ORDER_VAR: ".".join(o_list_primary)}),
133
189
  "url_remove": cl.get_query_string({ORDER_VAR: ".".join(o_list_remove)}),
134
190
  "url_toggle": cl.get_query_string({ORDER_VAR: ".".join(o_list_toggle)}),
135
- "class_attrib": format_html(' class="{}"', " ".join(th_classes))
191
+ "class_attrib": format_html("{}", " ".join(th_classes))
136
192
  if th_classes
137
193
  else "",
138
194
  }
@@ -157,29 +213,8 @@ def items_for_result(cl: ChangeList, result: HttpRequest, form) -> SafeText:
157
213
  for field_index, field_name in enumerate(cl.list_display):
158
214
  empty_value_display = cl.model_admin.get_empty_value_display()
159
215
  row_classes = [
160
- "field-%s" % _coerce_field_name(field_name, field_index),
161
- "align-middle",
162
- "flex",
163
- "border-t",
164
- "border-gray-200",
165
- "font-normal",
166
- "px-3",
167
- "py-2",
168
- "text-left",
169
- "text-sm",
170
- "before:flex",
171
- "before:capitalize",
172
- "before:content-[attr(data-label)]",
173
- "before:items-center",
174
- "before:mr-auto",
175
- "before:text-gray-500",
176
- "first:border-t-0",
177
- "dark:before:text-gray-400",
178
- "lg:before:hidden",
179
- "lg:first:border-t",
180
- "lg:py-3",
181
- "lg:table-cell",
182
- "dark:border-gray-800",
216
+ f"field-{_coerce_field_name(field_name, field_index)}",
217
+ *ROW_CLASSES,
183
218
  ]
184
219
 
185
220
  try:
@@ -192,27 +227,7 @@ def items_for_result(cl: ChangeList, result: HttpRequest, form) -> SafeText:
192
227
  )
193
228
  if f is None or f.auto_created:
194
229
  if field_name == "action_checkbox":
195
- row_classes = [
196
- "action-checkbox",
197
- "align-middle",
198
- "flex",
199
- "items-center",
200
- "px-3",
201
- "py-2",
202
- "text-left",
203
- "text-sm",
204
- "before:block",
205
- "before:capitalize",
206
- "before:content-[attr(data-label)]",
207
- "before:mr-auto",
208
- "before:text-gray-500",
209
- "lg:before:hidden",
210
- "lg:border-t",
211
- "lg:border-gray-200",
212
- "lg:table-cell",
213
- "dark:before:text-gray-400",
214
- "dark:lg:border-gray-800",
215
- ]
230
+ row_classes = CHECKBOX_CLASSES
216
231
  boolean = getattr(attr, "boolean", False)
217
232
  label = getattr(attr, "label", False)
218
233
  header = getattr(attr, "header", False)
@@ -239,7 +254,7 @@ def items_for_result(cl: ChangeList, result: HttpRequest, form) -> SafeText:
239
254
  f, (models.DateField, models.TimeField, models.ForeignKey)
240
255
  ):
241
256
  row_classes.append("nowrap")
242
- row_class = mark_safe(' class="%s"' % " ".join(row_classes))
257
+ row_class = mark_safe(f' class="{" ".join(row_classes)}"')
243
258
  # If list_display_links not defined, add the link tag to the first field
244
259
 
245
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
- pass
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,7 +436,12 @@ class UnfoldAdminBigIntegerFieldWidget(AdminBigIntegerFieldWidget):
433
436
 
434
437
 
435
438
  class UnfoldAdminNullBooleanSelectWidget(NullBooleanSelect):
436
- pass
439
+ def __init__(self, attrs=None):
440
+ if attrs is None:
441
+ attrs = {}
442
+
443
+ attrs["class"] = " ".join(SELECT_CLASSES)
444
+ super().__init__(attrs)
437
445
 
438
446
 
439
447
  class UnfoldAdminSelect(Select):
@@ -446,7 +454,8 @@ class UnfoldAdminSelect(Select):
446
454
 
447
455
 
448
456
  class UnfoldAdminRadioSelectWidget(AdminRadioSelect):
449
- option_template_name = "admin/widgets/radio_option.html"
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)
@@ -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