django-unfold 0.40.0__py3-none-any.whl → 0.41.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.
Files changed (41) hide show
  1. {django_unfold-0.40.0.dist-info → django_unfold-0.41.0.dist-info}/METADATA +6 -1
  2. {django_unfold-0.40.0.dist-info → django_unfold-0.41.0.dist-info}/RECORD +41 -38
  3. {django_unfold-0.40.0.dist-info → django_unfold-0.41.0.dist-info}/WHEEL +1 -1
  4. unfold/admin.py +5 -0
  5. unfold/contrib/filters/forms.py +2 -2
  6. unfold/contrib/forms/templates/unfold/forms/array.html +2 -2
  7. unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage.html +1 -1
  8. unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_group.html +1 -1
  9. unfold/contrib/guardian/templates/admin/guardian/model/obj_perms_manage_user.html +1 -1
  10. unfold/contrib/import_export/templates/admin/import_export/export.html +1 -1
  11. unfold/contrib/import_export/templates/admin/import_export/import.html +1 -1
  12. unfold/contrib/simple_history/templates/simple_history/object_history_form.html +1 -1
  13. unfold/static/admin/js/inlines.js +439 -0
  14. unfold/static/unfold/css/styles.css +1 -1
  15. unfold/static/unfold/fonts/material-symbols/Material-Symbols-Outlined.woff2 +0 -0
  16. unfold/static/unfold/fonts/material-symbols/styles.css +1 -2
  17. unfold/static/unfold/js/alpine.sort.js +1 -0
  18. unfold/static/unfold/js/app.js +36 -10
  19. unfold/static/unfold/js/select2.init.js +1 -1
  20. unfold/styles.css +5 -1
  21. unfold/templates/admin/app_index.html +1 -1
  22. unfold/templates/admin/auth/user/change_password.html +1 -1
  23. unfold/templates/admin/base.html +1 -1
  24. unfold/templates/admin/change_form.html +1 -1
  25. unfold/templates/admin/change_list.html +46 -44
  26. unfold/templates/admin/delete_confirmation.html +4 -9
  27. unfold/templates/admin/delete_selected_confirmation.html +4 -8
  28. unfold/templates/admin/edit_inline/stacked.html +15 -6
  29. unfold/templates/admin/edit_inline/tabular.html +36 -19
  30. unfold/templates/admin/object_history.html +1 -1
  31. unfold/templates/admin/submit_line.html +8 -8
  32. unfold/templates/registration/password_change_done.html +1 -1
  33. unfold/templates/registration/password_change_form.html +1 -1
  34. unfold/templates/unfold/change_list_filter.html +7 -9
  35. unfold/templates/unfold/helpers/delete_submit_line.html +11 -0
  36. unfold/templates/unfold/helpers/display_header.html +8 -3
  37. unfold/templates/unfold/helpers/field_readonly_value.html +1 -1
  38. unfold/templates/unfold/helpers/fieldset_row.html +2 -0
  39. unfold/templates/unfold/layouts/skeleton.html +1 -0
  40. unfold/templates/unfold/widgets/url.html +7 -5
  41. {django_unfold-0.40.0.dist-info → django_unfold-0.41.0.dist-info}/LICENSE.md +0 -0
@@ -12,6 +12,21 @@ window.addEventListener("load", (e) => {
12
12
  warnWithoutSaving();
13
13
  });
14
14
 
15
+ /*************************************************************
16
+ * Alpine.sort.js callback after sorting
17
+ *************************************************************/
18
+ const sortRecords = (e) => {
19
+ const orderingField = e.from.dataset.orderingField;
20
+
21
+ const weightInputs = Array.from(
22
+ e.from.querySelectorAll(`.has_original input[name$=-${orderingField}]`)
23
+ );
24
+
25
+ weightInputs.forEach((input, index) => {
26
+ input.value = index;
27
+ });
28
+ };
29
+
15
30
  /*************************************************************
16
31
  * Warn without saving
17
32
  *************************************************************/
@@ -120,15 +135,26 @@ const dateTimeShortcutsOverlay = () => {
120
135
  * File upload path
121
136
  *************************************************************/
122
137
  const fileInputUpdatePath = () => {
123
- Array.from(document.querySelectorAll("input[type=file]")).forEach((input) => {
124
- input.addEventListener("change", (e) => {
125
- const parts = e.target.value.split("\\");
126
- const placeholder =
127
- input.parentNode.parentNode.parentNode.querySelector(
128
- "input[type=text]"
129
- );
130
- placeholder.setAttribute("value", parts[parts.length - 1]);
131
- });
138
+ const observer = new MutationObserver((mutations) => {
139
+ for (const mutation of mutations) {
140
+ if (mutation.type === "childList") {
141
+ for (const input of document.querySelectorAll("input[type=file]")) {
142
+ input.addEventListener("change", (e) => {
143
+ const parts = e.target.value.split("\\");
144
+ const placeholder =
145
+ input.parentNode.parentNode.parentNode.querySelector(
146
+ "input[type=text]"
147
+ );
148
+ placeholder.setAttribute("value", parts[parts.length - 1]);
149
+ });
150
+ }
151
+ }
152
+ }
153
+ });
154
+
155
+ observer.observe(document.body, {
156
+ childList: true,
157
+ subtree: true,
132
158
  });
133
159
  };
134
160
 
@@ -153,7 +179,7 @@ const submitSearch = () => {
153
179
  }, {});
154
180
 
155
181
  if (searchString) {
156
- queryParams["q"] = searchString;
182
+ queryParams["q"] = encodeURIComponent(searchString);
157
183
  }
158
184
 
159
185
  const result = Object.entries(queryParams)
@@ -10,6 +10,6 @@
10
10
  };
11
11
 
12
12
  $(function () {
13
- $(".admin-autocomplete").djangoCustomSelect2();
13
+ $(".unfold-admin-autocomplete.admin-autocomplete").djangoCustomSelect2();
14
14
  });
15
15
  }
unfold/styles.css CHANGED
@@ -60,6 +60,10 @@ html {
60
60
  @apply text-red-600;
61
61
  }
62
62
 
63
+ .sortable-ghost {
64
+ @apply opacity-50;
65
+ }
66
+
63
67
  /*******************************************************
64
68
  Icons
65
69
  *******************************************************/
@@ -287,7 +291,7 @@ h3 span:nth-child(3) {
287
291
  Autocomplete
288
292
  *******************************************************/
289
293
  .select2.select2-container {
290
- @apply bg-white border max-w-2xl relative rounded-md shadow-sm !w-full dark:bg-gray-900 dark:border-gray-700;
294
+ @apply bg-white border max-w-2xl !min-h-9.5 relative rounded-md shadow-sm !w-full dark:bg-gray-900 dark:border-gray-700;
291
295
  }
292
296
 
293
297
  .errors .select2.select2-container {
@@ -10,7 +10,7 @@
10
10
  {% block breadcrumbs %}
11
11
  <div class="px-12">
12
12
  <div class="container mb-12 mx-auto -my-3">
13
- <ul class="flex">
13
+ <ul class="flex flex-wrap">
14
14
  {% url 'admin:index' as link %}
15
15
  {% trans 'Home' as name %}
16
16
  {% include 'unfold/helpers/breadcrumb_item.html' with link=link name=name %}
@@ -8,7 +8,7 @@
8
8
  {% block breadcrumbs %}
9
9
  <div class="px-12">
10
10
  <div class="container mb-12 mx-auto -my-3">
11
- <ul class="flex">
11
+ <ul class="flex flex-wrap">
12
12
  {% url 'admin:index' as link %}
13
13
  {% trans 'Home' as name %}
14
14
  {% include 'unfold/helpers/breadcrumb_item.html' with link=link name=name %}
@@ -18,7 +18,7 @@
18
18
  {% block breadcrumbs %}
19
19
  <div class="px-4 lg:px-12">
20
20
  <div class="container mb-12 mx-auto -my-3">
21
- <ul class="flex">
21
+ <ul class="flex flex-wrap">
22
22
  {% url 'admin:index' as link %}
23
23
  {% trans 'Home' as name %}
24
24
  {% include 'unfold/helpers/breadcrumb_item.html' with link=link name=name %}
@@ -13,7 +13,7 @@
13
13
  {% block breadcrumbs %}
14
14
  <div class="px-4 lg:px-12">
15
15
  <div class="container mb-6 mx-auto -my-3 lg:mb-12">
16
- <ul class="flex">
16
+ <ul class="flex flex-wrap">
17
17
  {% url 'admin:index' as link %}
18
18
  {% trans 'Home' as name %}
19
19
  {% include 'unfold/helpers/breadcrumb_item.html' with link=link name=name %}
@@ -26,7 +26,7 @@
26
26
  {% block breadcrumbs %}
27
27
  <div class="px-4 lg:px-12">
28
28
  <div class="{% if not cl.model_admin.list_fullwidth %}container{% endif %} mb-6 mx-auto -my-3 lg:mb-12">
29
- <ul class="flex">
29
+ <ul class="flex flex-wrap">
30
30
  {% url 'admin:index' as link %}
31
31
  {% trans 'Home' as name %}
32
32
  {% include 'unfold/helpers/breadcrumb_item.html' with link=link name=name %}
@@ -62,60 +62,62 @@
62
62
  {% endif %}
63
63
 
64
64
  <div class="flex -mx-4 module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
65
- <div class="changelist-form-container flex-grow min-w-0 px-4" x-data="{ filterOpen: false }">
66
- {% block date_hierarchy %}
67
- {% if cl.date_hierarchy %}
68
- {% date_hierarchy cl %}
65
+ <div class="changelist-form-container flex flex-row flex-grow gap-4 min-w-0 px-4" x-data="{ filterOpen: false }">
66
+ <div class="flex-grow min-w-0">
67
+ {% block date_hierarchy %}
68
+ {% if cl.date_hierarchy %}
69
+ {% date_hierarchy cl %}
70
+ {% endif %}
71
+ {% endblock %}
72
+
73
+ {% if cl.model_admin.list_before_template %}
74
+ {% include cl.model_admin.list_before_template %}
69
75
  {% endif %}
70
- {% endblock %}
71
76
 
72
- {% if cl.model_admin.list_before_template %}
73
- {% include cl.model_admin.list_before_template %}
74
- {% endif %}
77
+ <form id="changelist-form" method="post"{% if cl.formset and cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %} novalidate>
78
+ {% csrf_token %}
75
79
 
76
- <form id="changelist-form" method="post"{% if cl.formset and cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %} novalidate>
77
- {% csrf_token %}
80
+ {% if cl.formset %}
81
+ <div>{{ cl.formset.management_form }}</div>
82
+ {% endif %}
78
83
 
79
- {% if cl.formset %}
80
- <div>{{ cl.formset.management_form }}</div>
81
- {% endif %}
84
+ {% block result_list %}
85
+ {% if actions_on_top %}
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 lg:flex-row dark:bg-gray-800">
88
+ {% block search %}
89
+ {% search_form cl %}
90
+ {% endblock %}
82
91
 
83
- {% block result_list %}
84
- {% if actions_on_top %}
85
- {% if cl.search_fields or action_form or cl.has_filters %}
86
- <div class="bg-gray-50 flex flex-col gap-3 mb-4 p-3 rounded-md lg:flex-row dark:bg-gray-800">
87
- {% block search %}
88
- {% search_form cl %}
89
- {% endblock %}
90
-
91
- {% if action_form %}
92
- {% admin_actions %}
93
- {% endif %}
94
-
95
- {% block filters %}
96
- {% if cl.has_filters %}
97
- <a class="{% if cl.has_active_filters %}bg-primary-600 border-primary-600 text-white{% else %}bg-white dark:bg-gray-900 dark:border-gray-700{% 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">
98
- {% trans "Filters" %}
99
-
100
- <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-500 dark:group-hover:text-gray-400 dark:text-gray-500{% endif %}">filter_list</span>
101
- </a>
92
+ {% if action_form %}
93
+ {% admin_actions %}
102
94
  {% endif %}
103
- {% endblock %}
104
- </div>
95
+
96
+ {% block filters %}
97
+ {% if cl.has_filters %}
98
+ <a class="{% if cl.has_active_filters %}bg-primary-600 border-primary-600 text-white{% else %}bg-white dark:bg-gray-900 dark:border-gray-700{% 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 {% if not cl.model_admin.list_filter_sheet %}2xl:hidden{% endif %}" x-on:click="filterOpen = true" x-on:keydown.escape.window="filterOpen = false">
99
+ {% trans "Filters" %}
100
+
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-500 dark:group-hover:text-gray-400 dark:text-gray-500{% endif %}">filter_list</span>
102
+ </a>
103
+ {% endif %}
104
+ {% endblock %}
105
+ </div>
106
+ {% endif %}
105
107
  {% endif %}
106
- {% endif %}
107
108
 
108
- {% unfold_result_list cl %}
109
+ {% unfold_result_list cl %}
109
110
 
110
- {% block pagination %}
111
- {% pagination cl %}
111
+ {% block pagination %}
112
+ {% pagination cl %}
113
+ {% endblock %}
112
114
  {% endblock %}
113
- {% endblock %}
114
- </form>
115
+ </form>
115
116
 
116
- {% if cl.model_admin.list_after_template %}
117
- {% include cl.model_admin.list_after_template %}
118
- {% endif %}
117
+ {% if cl.model_admin.list_after_template %}
118
+ {% include cl.model_admin.list_after_template %}
119
+ {% endif %}
120
+ </div>
119
121
 
120
122
  {% if cl.has_filters %}
121
123
  {% include "unfold/change_list_filter.html" %}
@@ -10,9 +10,9 @@
10
10
  {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %}
11
11
 
12
12
  {% block breadcrumbs %}
13
- <div class="px-12">
14
- <div class="container mb-12 mx-auto -my-3">
15
- <ul class="flex">
13
+ <div class="px-4 lg:px-12">
14
+ <div class="container mb-6 mx-auto -my-3 lg:mb-12">
15
+ <ul class="flex flex-wrap">
16
16
  {% url 'admin:index' as link %}
17
17
  {% trans 'Home' as name %}
18
18
  {% include 'unfold/helpers/breadcrumb_item.html' with link=link name=name %}
@@ -95,12 +95,7 @@
95
95
  <input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">
96
96
  {% endif %}
97
97
 
98
-
99
- <a href="#" class="border cancel-link font-medium hidden mb-3 px-3 py-2 rounded-md w-full hover:bg-gray-50 lg:block lg:mb-0 lg:mr-3 lg:w-auto dark:border-gray-700 dark:text-font-default-dark dark:hover:text-gray-200 dark:hover:bg-gray-900">
100
- {% translate "No, take me back" %}
101
- </a>
102
-
103
- <input type="submit" value="{% translate 'Yes, I’m sure' %}" class="bg-red-600 cursor-pointer font-medium ml-auto px-3 py-2 rounded-md text-white dark:bg-red-500/20 dark:text-red-500">
98
+ {% include "unfold/helpers/delete_submit_line.html" %}
104
99
  </div>
105
100
  </form>
106
101
  </div>
@@ -10,9 +10,9 @@
10
10
  {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %}
11
11
 
12
12
  {% block breadcrumbs %}
13
- <div class="px-12">
14
- <div class="container mb-12 mx-auto -my-3">
15
- <ul class="flex">
13
+ <div class="px-4 lg:px-12">
14
+ <div class="container mb-6 mx-auto -my-3 lg:mb-12">
15
+ <ul class="flex flex-wrap">
16
16
  {% url 'admin:index' as link %}
17
17
  {% trans 'Home' as name %}
18
18
  {% include 'unfold/helpers/breadcrumb_item.html' with link=link name=name %}
@@ -89,11 +89,7 @@
89
89
  <input type="hidden" name="action" value="delete_selected">
90
90
  <input type="hidden" name="post" value="yes">
91
91
 
92
- <a href="#" class="border cancel-link font-medium hidden mb-3 px-3 py-2 rounded-md transition-all w-full hover:bg-gray-50 lg:block lg:mb-0 lg:mr-3 lg:w-auto dark:border-gray-700 dark:text-font-default-dark dark:hover:text-gray-200 dark:hover:bg-gray-900">
93
- {% translate "No, take me back" %}
94
- </a>
95
-
96
- <input type="submit" value="{% translate 'Yes, I’m sure' %}" class="bg-red-600 cursor-pointer font-medium ml-auto px-3 py-2 rounded-md text-white dark:bg-red-500/20 dark:text-red-500">
92
+ {% include "unfold/helpers/delete_submit_line.html" %}
97
93
  </div>
98
94
  </form>
99
95
  </div>
@@ -13,15 +13,24 @@
13
13
  {{ inline_admin_formset.formset.management_form }}
14
14
  {% include "unfold/helpers/messages/error.html" with errors=inline_admin_formset.formset.non_form_errors %}
15
15
 
16
+ <div class="border border-gray-200 mb-6 overflow-hidden rounded-md shadow-sm w-full dark:border-gray-800 *:border-t first:*:border-t-0 *:border-gray-200 dark:*:border-gray-800"
17
+ {% if inline_admin_formset.opts.ordering_field %}
18
+ data-ordering-field="{{ inline_admin_formset.opts.ordering_field }}" x-on:end="sortRecords" x-sort.ghost
19
+ {% endif %}>
16
20
 
17
- <div class="border border-gray-200 mb-6 overflow-hidden rounded-md shadow-sm w-full dark:border-gray-800">
18
21
  {% for inline_admin_form in inline_admin_formset %}
19
- <div class="inline-related group inline-stacked {% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}{% if forloop.last and inline_admin_formset.has_add_permission %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
22
+ <div x-sort:item class="inline-related group inline-stacked {% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}{% if forloop.last and inline_admin_formset.has_add_permission %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
20
23
  {% if not inline_admin_formset.opts.hide_title or inline_admin_formset.formset.can_delete and inline_admin_formset.has_delete_permission %}
21
- <h3 class="bg-gray-50 border-b {% if not forloop.first %}border-t{% endif %} border-gray-200 flex font-medium items-center mb-3 px-3 py-2 text-sm dark:bg-white/[.02] dark:border-gray-800">
22
- <span class="mr-2">
23
- {{ inline_admin_formset.opts.verbose_name|capfirst }}:
24
- </span>
24
+ <h3 class="bg-gray-50 border-b border-gray-200 flex font-medium items-center gap-2 mb-3 px-3 py-2 text-sm dark:bg-white/[.02] dark:border-gray-800">
25
+ {% if inline_admin_formset.opts.ordering_field %}
26
+ {% if inline_admin_form.original %}
27
+ <span class="material-symbols-outlined cursor-pointer" x-sort:handle>drag_indicator</span>
28
+ {% else %}
29
+ <span class="-mr-2" x-sort:handle></span>
30
+ {% endif %}
31
+ {% endif %}
32
+
33
+ {{ inline_admin_formset.opts.verbose_name|capfirst }}:
25
34
 
26
35
  <span class="inline_label font-semibold text-font-important-light dark:text-font-important-dark">
27
36
  {% if inline_admin_form.original and inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %}
@@ -16,12 +16,17 @@
16
16
  {{ inline_admin_formset.formset.non_form_errors }}
17
17
 
18
18
  <div class="border border-gray-200 mb-6 overflow-x-auto rounded-md shadow-sm dark:border-gray-800" data-simplebar data-simplebar-auto-hide="false">
19
- <table class="border-spacing-none border-separate w-full">
19
+ <table class="border-spacing-none border-separate w-full"
20
+ {% if inline_admin_formset.opts.ordering_field %}
21
+ data-ordering-field="{{ inline_admin_formset.opts.ordering_field }}"
22
+ x-on:end="sortRecords"
23
+ x-sort.ghost
24
+ {% endif %}>
20
25
  <thead class="hidden lg:table-header-group">
21
26
  <tr>
22
27
  {% for field in inline_admin_formset.fields %}
23
28
  {% if not field.widget.is_hidden %}
24
- <th class="column-{{ field.name }}{% if field.required %} required{% endif %} align-middle border-b border-gray-200 font-semibold px-3 py-2 text-left text-font-important-light text-sm whitespace-nowrap dark:text-font-important-dark dark:border-gray-800">
29
+ <th class="column-{{ field.name }}{% if field.required %} required{% endif %} align-middle border-b border-gray-200 font-semibold px-3 py-2 text-left text-font-important-light text-sm whitespace-nowrap dark:text-font-important-dark dark:border-gray-800 {% if inline_admin_formset.opts.ordering_field and field.name == inline_admin_formset.opts.ordering_field and inline_admin_formset.opts.hide_ordering_field %}hidden{% endif %}">
25
30
  <span class="flex flex-row items-center">
26
31
  {{ field.label|capfirst }}
27
32
 
@@ -41,8 +46,8 @@
41
46
  </tr>
42
47
  </thead>
43
48
 
44
- <tbody>
45
- {% for inline_admin_form in inline_admin_formset %}
49
+ {% for inline_admin_form in inline_admin_formset %}
50
+ <tbody x-sort:item>
46
51
  {% if inline_admin_form.form.non_field_errors %}
47
52
  <tr class="row-form-errors group inline-tabular">
48
53
  <td colspan="{{ inline_admin_form|cell_count }}">
@@ -118,20 +123,32 @@
118
123
  {% with is_last_col=forloop.last %}
119
124
  {% for field in line %}
120
125
  {% if field.is_readonly or not field.field.is_hidden %}
121
- <td{% if field.field.name %} class="field-{{ field.field.name }} group field-tabular {% 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:pr-4 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 }}">
122
- {% if field.is_readonly %}
123
- {% include "unfold/helpers/field_readonly_value.html" with tabular=1 %}
124
- {% else %}
125
- {{ field.field }}
126
-
127
- {% if field.field.errors|length > 0 %}
128
- <div class="mt-1 text-red-600 text-sm dark:text-red-500">
129
- {% for error in field.field.errors %}
130
- {{ error }}
131
- {% endfor %}
132
- </div>
126
+ <td{% if field.field.name %} class="field-{{ field.field.name }} group field-tabular {% 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:pr-4 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 %} {% if inline_admin_formset.opts.ordering_field and field.field.name == inline_admin_formset.opts.ordering_field and inline_admin_formset.opts.hide_ordering_field %}!hidden{% endif %}"{% endif %} data-label="{{ field.field.label }}">
127
+ <div class="flex flex-row gap-3 items-center">
128
+ {% if forloop.parentloop.counter == 1 and forloop.counter == 1 %}
129
+ {% if inline_admin_formset.opts.ordering_field %}
130
+ {% if inline_admin_form.original %}
131
+ <span class="material-symbols-outlined cursor-pointer" x-sort:handle>drag_indicator</span>
132
+ {% else %}
133
+ <span class="-mr-3" x-sort:handle></span>
134
+ {% endif %}
135
+ {% endif %}
133
136
  {% endif %}
134
- {% endif %}
137
+
138
+ {% if field.is_readonly %}
139
+ {% include "unfold/helpers/field_readonly_value.html" with tabular=1 %}
140
+ {% else %}
141
+ {{ field.field }}
142
+
143
+ {% if field.field.errors|length > 0 %}
144
+ <div class="mt-1 text-red-600 text-sm dark:text-red-500">
145
+ {% for error in field.field.errors %}
146
+ {{ error }}
147
+ {% endfor %}
148
+ </div>
149
+ {% endif %}
150
+ {% endif %}
151
+ </div>
135
152
  </td>
136
153
  {% endif %}
137
154
  {% endfor %}
@@ -152,8 +169,8 @@
152
169
  {% endif %}
153
170
  {% endwith %}
154
171
  </tr>
155
- {% endfor %}
156
- </tbody>
172
+ </tbody>
173
+ {% endfor %}
157
174
  </table>
158
175
  </div>
159
176
  </fieldset>
@@ -4,7 +4,7 @@
4
4
  {% block breadcrumbs %}
5
5
  <div class="px-4 lg:px-12">
6
6
  <div class="container mb-12 mx-auto -my-3">
7
- <ul class="flex">
7
+ <ul class="flex flex-wrap">
8
8
  {% url 'admin:index' as link %}
9
9
  {% trans 'Home' as name %}
10
10
  {% include 'unfold/helpers/breadcrumb_item.html' with link=link name=name %}
@@ -1,8 +1,8 @@
1
1
  {% load i18n admin_urls %}
2
2
 
3
- <div {% if not is_popup %}id="submit-row"{% endif %} class="relative z-20 {% if not is_popup %} mt-20 lg:mt-8{% endif %}">
4
- <div class="{% if not is_popup %}bottom-0 fixed left-0 right-0{% endif %}" {% if not is_popup %}x-bind:class="{'xl:left-0': !sidebarDesktopOpen, 'xl:left-72': sidebarDesktopOpen}"{% endif %}>
5
- <div class="bg-white dark:bg-gray-900 {% if not is_popup %}border-t px-4 py-4 relative scrollable-top lg:px-12 dark:border-gray-800{% endif %}">
3
+ <div {% if not is_popup %}id="submit-row"{% endif %} class="relative z-20 {% if not is_popup %} mt-8{% endif %}">
4
+ <div class="{% if not is_popup %}lg:bottom-0 lg:fixed lg:left-0 lg:right-0{% endif %}" {% if not is_popup %}x-bind:class="{'xl:left-0': !sidebarDesktopOpen, 'xl:left-72': sidebarDesktopOpen}"{% endif %}>
5
+ <div class="bg-white dark:bg-gray-900 {% if not is_popup %}lg:border-t lg:py-4 relative scrollable-top lg:px-12 dark:border-gray-800{% endif %}">
6
6
  <div class="container flex flex-col-reverse gap-3 items-center mx-auto lg:flex-row-reverse">
7
7
  {% block submit-row %}
8
8
  {% if show_save %}
@@ -18,7 +18,7 @@
18
18
  {% endfor %}
19
19
 
20
20
  {% if show_save_and_continue %}
21
- <button type="submit" name="_continue" 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">
21
+ <button type="submit" name="_continue" class="border font-medium 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">
22
22
  {% if can_change %}
23
23
  {% translate 'Save and continue editing' %}
24
24
  {% else %}
@@ -30,19 +30,19 @@
30
30
  {% if show_close %}
31
31
  {% url opts|admin_urlname:'changelist' as changelist_url %}
32
32
 
33
- <a href="{% add_preserved_filters changelist_url %}" 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">
33
+ <a href="{% add_preserved_filters changelist_url %}" class="border font-medium 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">
34
34
  {% translate 'Close' %}
35
35
  </a>
36
36
  {% endif %}
37
37
 
38
38
  {% if show_save_and_add_another %}
39
- <button type="submit" name="_addanother" 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">
39
+ <button type="submit" name="_addanother" class="border font-medium 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">
40
40
  {% translate 'Save and add another' %}
41
41
  </button>
42
42
  {% endif %}
43
43
 
44
44
  {% if show_save_as_new %}
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">
45
+ <button type="submit" name="_saveasnew" class="border font-medium 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 %}
@@ -51,7 +51,7 @@
51
51
  {% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
52
52
 
53
53
  <p class="deletelink-box mr-auto w-full lg:w-auto">
54
- <a href="{% add_preserved_filters delete_url %}" class="block border border-red-500 font-medium px-3 py-2 rounded-md text-center text-red-500 whitespace-nowrap dark:border-transparent dark:bg-red-500/20 dark:text-red-500">
54
+ <a href="{% add_preserved_filters delete_url %}" class="bg-red-600 flex items-center justify-center font-medium h-9.5 ml-auto px-3 py-2 rounded-md text-white dark:bg-red-500/20 dark:text-red-500">
55
55
  {% translate "Delete" %} {{ opts.verbose_name }}
56
56
  </a>
57
57
  </p>
@@ -7,7 +7,7 @@
7
7
  {% block breadcrumbs %}
8
8
  <div class="px-12">
9
9
  <div class="container mb-12 mx-auto -my-3">
10
- <ul class="flex">
10
+ <ul class="flex flex-wrap">
11
11
  {% url 'admin:index' as link %}
12
12
  {% trans 'Home' as name %}
13
13
  {% include 'unfold/helpers/breadcrumb_item.html' with link=link name=name %}
@@ -7,7 +7,7 @@
7
7
  {% block breadcrumbs %}
8
8
  <div class="px-12">
9
9
  <div class="container mb-12 mx-auto -my-3">
10
- <ul class="flex">
10
+ <ul class="flex flex-wrap">
11
11
  {% url 'admin:index' as link %}
12
12
  {% trans 'Home' as name %}
13
13
  {% include 'unfold/helpers/breadcrumb_item.html' with link=link name=name %}
@@ -1,11 +1,11 @@
1
1
  {% load i18n admin_list %}
2
2
 
3
- <div id="changelist-filter" class="backdrop-blur-sm bg-opacity-80 bg-gray-900 flex fixed inset-0 z-50" x-show="filterOpen">
4
- <label for="show-filters" id="changelist-filter-close" class="flex-grow"></label>
3
+ <div id="changelist-filter" class="backdrop-blur-sm bg-opacity-80 bg-gray-900 flex inset-0 z-50 fixed {% if not cl.model_admin.list_filter_sheet %}2xl:bg-transparent 2xl:relative 2xl:!block 2xl:z-10{% endif %}" x-show="filterOpen">
4
+ <label for="show-filters" id="changelist-filter-close" class="flex-grow {% if not cl.model_admin.list_filter_sheet %}2xl:hidden{% endif %}" x-on:click="filterOpen = false"></label>
5
5
 
6
- <div class="bg-white flex mx-4 my-4 overflow-hidden rounded shadow-sm w-96 dark:bg-gray-800" x-on:click.outside="filterOpen = false">
6
+ <div class="bg-white flex m-4 overflow-hidden rounded shadow-sm w-80 dark:bg-gray-800 {% if not cl.model_admin.list_filter_sheet %} 2xl:border 2xl:sticky 2xl:top-4 2xl:dark:border-gray-800 2xl:bg-transparent 2xl:dark:!bg-transparent 2xl:m-0{% endif %}">
7
7
  <div class="flex-grow h-full overflow-auto relative">
8
- <h3 class="border-b flex font-semibold mb-6 px-6 py-4 text-font-important-light text-sm dark:text-font-important-dark dark:border-gray-700">
8
+ <h3 class="border-b flex font-semibold mb-4 px-4 py-4 text-font-important-light text-sm dark:text-font-important-dark dark:border-gray-800">
9
9
  {% trans "Filter" %}
10
10
 
11
11
  {% if cl.is_facets_optional %}
@@ -30,15 +30,14 @@
30
30
  </a>
31
31
  {% endif %}
32
32
 
33
- <a class="cursor-pointer material-symbols-outlined md-18 ml-auto pl-4 text-gray-400 transition-colors hover:text-gray-500 dark:text-gray-500 dark:hover:text-gray-400" x-on:click="filterOpen = false">
33
+ <a class="cursor-pointer material-symbols-outlined md-18 ml-auto pl-4 text-gray-400 transition-colors hover:text-gray-500 dark:text-gray-500 dark:hover:text-gray-400 {% if not cl.model_admin.list_filter_sheet %}2xl:hidden{% endif %}" x-on:click="filterOpen = false">
34
34
  close
35
35
  </a>
36
36
  </h3>
37
37
 
38
- <div class="px-6{% if cl.model_admin.list_filter_submit %} pb-16{% endif %}">
38
+ <div class="px-4{% if cl.model_admin.list_filter_submit %} {% endif %}">
39
39
  {% if cl.model_admin.list_filter_submit %}
40
40
  <form id="filter-form" method="get">
41
-
42
41
  {% if request.GET.q %}
43
42
  <input type="hidden" name="q" value="{{ request.GET.q }}">
44
43
  {% endif %}
@@ -48,13 +47,12 @@
48
47
  {% endif %}
49
48
  {% endif %}
50
49
 
51
-
52
50
  {% for spec in cl.filter_specs %}
53
51
  {% admin_list_filter cl spec %}
54
52
  {% endfor %}
55
53
 
56
54
  {% if cl.model_admin.list_filter_submit %}
57
- <div class="bg-gray-50 border-gray-200 border-t bottom-4 fixed px-6 py-3 right-4 rounded-b w-96 dark:bg-gray-700 dark:border-gray-600">
55
+ <div class="bottom-4 absolute left-4 right-4 {% if not cl.model_admin.list_filter_sheet %}2xl:left-0 2xl:right-0 2xl:relative 2xl:mt-8{% endif %}">
58
56
  <button type="submit" class="bg-primary-600 block border border-transparent font-medium px-3 py-2 rounded-md self-end text-sm text-white w-full">
59
57
  {% trans "Apply Filters" %}
60
58
  </button>
@@ -0,0 +1,11 @@
1
+ {% load i18n %}
2
+
3
+ <div class="flex flex-col gap-3 items-center w-full lg:flex-row">
4
+ <a href="#" class="border cancel-link flex items-center justify-center font-medium px-3 py-2 rounded-md w-full hover:bg-gray-50 lg:block lg:mb-0 lg:w-auto dark:border-gray-700 dark:text-font-default-dark dark:hover:text-gray-200 dark:hover:bg-gray-900">
5
+ {% translate "No, take me back" %}
6
+ </a>
7
+
8
+ <button type="submit" class="bg-red-600 cursor-pointer flex items-center justify-center font-medium h-9.5 ml-0 px-3 py-2 rounded-md text-white w-full lg:ml-auto lg:w-auto dark:bg-red-500/20 dark:text-red-500">
9
+ {% translate "Yes, I’m sure" %}
10
+ </button>
11
+ </div>
@@ -2,9 +2,14 @@
2
2
 
3
3
  <span class="flex gap-4 items-center">
4
4
  {% if value.3 and value.3.path %}
5
- <span class="bg-center bg-cover bg-white flex font-medium justify-center overflow-hidden dark:bg-gray-900 dark:border-gray-700 {% if value.3.squared %}rounded-sm{% else %}rounded-full{% endif %}{% if not value.3.borderless %} border{% endif %}{% if not value.3.width or not value.3.height %} h-8 max-w-8 min-w-8{% endif %}">
6
- <img loading="lazy" src="{{ value.3.path }}" class="max-w-none object-cover" {% if value.3.width %}width="{{ value.3.width }}"{% endif %} {% if value.3.height %}height="{{ value.3.height }}"{% endif %} alt="{% trans "Record picture" %}" />
7
- </span>
5
+ {% if value.3.as_background %}
6
+ <span class="block bg-center bg-contain bg-no-repeat {% if not value.3.width %}w-16{% endif %} {% if not value.3.height %}h-12{% endif %}" style="background-image: url({{ value.3.path }}); {% if value.3.height %}height: {{ value.3.height }}px;{% endif %} {% if value.3.width %}width: {{ value.3.width }}px;{% endif %}">
7
+ </span>
8
+ {% else %}
9
+ <span class="bg-center bg-cover bg-white flex font-medium justify-center overflow-hidden dark:bg-gray-900 dark:border-gray-700 {% if value.3.squared %}rounded-sm{% else %}rounded-full{% endif %}{% if not value.3.borderless %} border{% endif %}{% if not value.3.width or not value.3.height %} h-8 max-w-8 min-w-8{% endif %}">
10
+ <img loading="lazy" src="{{ value.3.path }}" class="max-w-none object-cover" {% if value.3.width %}width="{{ value.3.width }}"{% endif %} {% if value.3.height %}height="{{ value.3.height }}"{% endif %} alt="{% trans "Record picture" %}" />
11
+ </span>
12
+ {% endif %}
8
13
  {% elif value.2 %}
9
14
  <span 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">
10
15
  {{ value.2 }}
@@ -1 +1 @@
1
- <div class="readonly max-w-4xl py-2 text-sm *:rounded-md {% if not adminform.model_admin.compressed_fields and not field.is_image %}bg-gray-50 border font-medium px-3 rounded-md shadow-sm dark:border-gray-700 dark:bg-gray-800{% endif %} {% if field.is_image %}inline-block [&_img]:rounded-md !py-0{% endif %}">{% if value %}{{ value }}{% elif field.contents %}{{ field.contents }}{% else %}-{% endif %}</div>
1
+ <div class="readonly {% if field.is_json %}max-w-4xl{% else %}max-w-2xl{% endif %} py-2 text-sm *:rounded-md {% if not adminform.model_admin.compressed_fields and not field.is_image %}bg-gray-50 border font-medium px-3 rounded-md shadow-sm dark:border-gray-700 dark:bg-gray-800{% endif %} {% if field.is_image %}inline-block [&_img]:rounded-md !py-0{% endif %}">{% if value %}{{ value }}{% elif field.contents %}{{ field.contents }}{% else %}-{% endif %}</div>
@@ -1,4 +1,6 @@
1
+
1
2
  <div class="form-row
3
+ {% for field in line %}{% if inline_admin_formset.opts.ordering_field and field.field.name == inline_admin_formset.opts.ordering_field and inline_admin_formset.opts.hide_ordering_field %} hidden{% endif %}{% endfor %}
2
4
  {% if adminform.model_admin.compressed_fields %} border-b border-gray-200 -mx-3 px-3 pt-3 first:pt-0 dark:border-gray-800 last:border-b-0{% endif %}
3
5
  {% if not line.fields|length == 1 %} flex flex-row flex-wrap gap-x-8{% endif %}
4
6
  {% if not line.has_visible_field %} hidden{% endif %}