django-unfold 0.25.0__py3-none-any.whl → 0.27.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. {django_unfold-0.25.0.dist-info → django_unfold-0.27.0.dist-info}/METADATA +61 -7
  2. {django_unfold-0.25.0.dist-info → django_unfold-0.27.0.dist-info}/RECORD +44 -31
  3. unfold/admin.py +13 -157
  4. unfold/contrib/forms/templates/unfold/forms/array.html +31 -0
  5. unfold/contrib/forms/widgets.py +58 -3
  6. unfold/contrib/import_export/forms.py +17 -0
  7. unfold/contrib/import_export/templates/admin/import_export/change_form.html +10 -0
  8. unfold/contrib/import_export/templates/admin/import_export/export.html +38 -3
  9. unfold/contrib/import_export/templates/admin/import_export/import_form.html +8 -12
  10. unfold/contrib/import_export/templates/admin/import_export/resource_fields_list.html +1 -1
  11. unfold/contrib/inlines/__init__.py +0 -0
  12. unfold/contrib/inlines/admin.py +141 -0
  13. unfold/contrib/inlines/apps.py +6 -0
  14. unfold/contrib/inlines/checks.py +18 -0
  15. unfold/contrib/inlines/forms.py +43 -0
  16. unfold/decorators.py +3 -0
  17. unfold/fields.py +200 -0
  18. unfold/forms.py +6 -0
  19. unfold/static/unfold/css/simplebar.css +230 -0
  20. unfold/static/unfold/css/styles.css +1 -1
  21. unfold/static/unfold/js/simplebar.js +10 -0
  22. unfold/styles.css +9 -1
  23. unfold/templates/admin/app_list.html +1 -1
  24. unfold/templates/admin/change_form.html +11 -11
  25. unfold/templates/admin/change_list_results.html +2 -2
  26. unfold/templates/admin/edit_inline/stacked.html +6 -6
  27. unfold/templates/admin/edit_inline/tabular.html +7 -9
  28. unfold/templates/admin/includes/fieldset.html +2 -32
  29. unfold/templates/unfold/helpers/app_list.html +1 -1
  30. unfold/templates/unfold/helpers/display_header.html +11 -8
  31. unfold/templates/unfold/helpers/field.html +20 -6
  32. unfold/templates/unfold/helpers/field_readonly.html +1 -3
  33. unfold/templates/unfold/helpers/field_readonly_value.html +1 -0
  34. unfold/templates/unfold/helpers/fieldset_row.html +53 -0
  35. unfold/templates/unfold/helpers/fieldsets_tabs.html +4 -4
  36. unfold/templates/unfold/helpers/form_label.html +1 -1
  37. unfold/templates/unfold/layouts/skeleton.html +2 -0
  38. unfold/templates/unfold/widgets/clearable_file_input.html +1 -1
  39. unfold/templates/unfold/widgets/foreign_key_raw_id.html +15 -0
  40. unfold/templates/unfold/widgets/textarea.html +1 -7
  41. unfold/templates/unfold/widgets/textarea_expandable.html +7 -0
  42. unfold/widgets.py +36 -3
  43. unfold/contrib/import_export/admin.py +0 -37
  44. {django_unfold-0.25.0.dist-info → django_unfold-0.27.0.dist-info}/LICENSE.md +0 -0
  45. {django_unfold-0.25.0.dist-info → django_unfold-0.27.0.dist-info}/WHEEL +0 -0
@@ -11,19 +11,19 @@
11
11
  </h2>
12
12
 
13
13
  {{ inline_admin_formset.formset.management_form }}
14
- {{ inline_admin_formset.formset.non_form_errors }}
14
+ {% include "unfold/helpers/messages/error.html" with errors=inline_admin_formset.formset.non_form_errors %}
15
15
 
16
16
  <div class="border border-gray-200 mb-6 overflow-hidden rounded-md shadow-sm text-gray-700 w-full dark:border-gray-800">
17
17
  {% for inline_admin_form in inline_admin_formset %}
18
18
  <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 %}">
19
- <h3 class="border-b {% if not forloop.first %}border-t{% endif %} border-gray-200 flex font-medium items-center mb-3 px-3 py-2 text-gray-400 text-sm dark:border-gray-800">
19
+ <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-gray-400 text-sm dark:bg-white/[.02] dark:border-gray-800">
20
20
  <span class="mr-2">
21
21
  {{ inline_admin_formset.opts.verbose_name|capfirst }}:
22
22
  </span>
23
23
 
24
- <span class="inline_label font-semibold text-gray-900">
24
+ <span class="inline_label font-semibold text-gray-900 dark:text-gray-200">
25
25
  {% if inline_admin_form.original and inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %}
26
- <a href="{% url inline_admin_form.model_admin.opts|admin_urlname:'change' inline_admin_form.original.pk|admin_urlquote %}" class="{% if inline_admin_formset.has_change_permission %}inlinechangelink{% else %}inlineviewlink{% endif %} font-medium ml-1 text-primary-600 underline text-xs">
26
+ <a href="{% url inline_admin_form.model_admin.opts|admin_urlname:'change' inline_admin_form.original.pk|admin_urlquote %}" class="{% if inline_admin_formset.has_change_permission %}inlinechangelink{% else %}inlineviewlink{% endif %} font-medium text-primary-600 underline">
27
27
  {{ inline_admin_form.original }}
28
28
  </a>
29
29
  {% else %}
@@ -42,7 +42,7 @@
42
42
  {% endif %}
43
43
 
44
44
  {% if inline_admin_formset.formset.can_delete and inline_admin_formset.has_delete_permission and inline_admin_form.original %}
45
- <span class="delete flex items-center ml-auto text-gray-500">
45
+ <span class="delete flex gap-2 items-center ml-auto text-gray-500">
46
46
  {{ inline_admin_form.deletion_field.field|add_css_class:form_classes.checkbox }} {{ inline_admin_form.deletion_field.label_tag }}
47
47
  </span>
48
48
  {% endif %}
@@ -52,7 +52,7 @@
52
52
 
53
53
  {% for fieldset in inline_admin_form %}
54
54
  <div class="px-3 -mb-5">
55
- {% include 'admin/includes/fieldset.html' %}
55
+ {% include 'admin/includes/fieldset.html' with stacked=1 %}
56
56
  </div>
57
57
  {% endfor %}
58
58
 
@@ -16,18 +16,18 @@
16
16
  {{ inline_admin_formset.formset.non_form_errors }}
17
17
 
18
18
  <div>
19
- {% if not inline_admin_formset.has_add_permission and inline_admin_formset.forms|length_is:0 %}
19
+ {% if not inline_admin_formset.has_add_permission and inline_admin_formset.forms|length == 0 %}
20
20
  <p class="mb-6 text-gray-500 text-sm dark:text-gray-400">
21
21
  {% trans "No records found." %}
22
22
  </p>
23
23
  {% else %}
24
- <div class="border border-gray-200 mb-6 overflow-x-auto rounded-md shadow-sm dark:border-gray-800">
24
+ <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">
25
25
  <table class="border-spacing-none border-separate text-gray-700 w-full">
26
26
  <thead class="hidden lg:table-header-group">
27
27
  <tr>
28
28
  {% for field in inline_admin_formset.fields %}
29
29
  {% if not field.widget.is_hidden %}
30
- <th class="column-{{ field.name }}{% if field.required %} required{% endif %} align-middle border-b border-gray-200 font-medium px-3 py-2 text-left text-gray-400 text-sm dark:border-gray-800">
30
+ <th class="column-{{ field.name }}{% if field.required %} required{% endif %} align-middle border-b border-gray-200 font-medium px-3 py-2 text-left text-gray-400 text-sm whitespace-nowrap dark:border-gray-800">
31
31
  <span class="flex flex-row items-center">
32
32
  {{ field.label|capfirst }}
33
33
 
@@ -40,7 +40,7 @@
40
40
  {% endfor %}
41
41
 
42
42
  {% if inline_admin_formset.formset.can_delete and inline_admin_formset.has_delete_permission %}
43
- <th class="align-middle border-b border-gray-200 font-medium px-3 py-2 text-left text-gray-400 text-sm lg:w-px dark:border-gray-800">
43
+ <th class="align-middle border-b border-gray-200 font-medium px-3 py-2 text-left text-gray-400 text-sm whitespace-nowrap lg:w-px dark:border-gray-800">
44
44
  {% translate "Delete?" %}
45
45
  </th>
46
46
  {% endif %}
@@ -59,12 +59,12 @@
59
59
 
60
60
  {% if inline_admin_form.original or inline_admin_form.show_url %}
61
61
  <tr>
62
- <td class="original" colspan="10">
62
+ <td class="original {% if inline_admin_form.original or inline_admin_form.show_url %}{% if inline_admin_form.original and inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model and inline_admin_formset.has_change_permission %}bg-gray-50 border-b border-gray-200 dark:bg-white/[.02] dark:border-gray-800{% endif %}{% endif %}" colspan="10">
63
63
  {% if inline_admin_form.original or inline_admin_form.show_url %}
64
64
  <p class="align-middle flex font-normal items-center leading-none px-3 text-gray-500 text-left text-sm whitespace-nowrap">
65
65
  {% if inline_admin_form.original %}
66
66
  {% if inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %}
67
- <a href="{% url inline_admin_form.model_admin.opts|admin_urlname:'change' inline_admin_form.original.pk|admin_urlquote %}" class="{% if inline_admin_formset.has_change_permission %}inlinechangelink{% else %}inlineviewlink{% endif %} font-medium mt-3 text-primary-600 underline text-xs">
67
+ <a href="{% url inline_admin_form.model_admin.opts|admin_urlname:'change' inline_admin_form.original.pk|admin_urlquote %}" class="{% if inline_admin_formset.has_change_permission %}inlinechangelink py-1.5{% else %}inlineviewlink mt-3{% endif %} font-medium text-primary-600 underline text-xs">
68
68
  {{ inline_admin_form.original }}
69
69
  </a>
70
70
  {% endif %}
@@ -112,9 +112,7 @@
112
112
  {% if field.is_readonly or not field.field.is_hidden %}
113
113
  <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 }}">
114
114
  {% if field.is_readonly %}
115
- <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">
116
- {{ field.contents }}
117
- </div>
115
+ {% include "unfold/helpers/field_readonly_value.html" with tabular=1 %}
118
116
  {% else %}
119
117
  {{ field.field }}
120
118
 
@@ -13,39 +13,9 @@
13
13
  </div>
14
14
  {% endif %}
15
15
 
16
- <div class="aligned border border-gray-200 mb-8 rounded-md pt-3 px-3 shadow-sm dark:border-gray-800">
16
+ <div class="aligned {% if stacked %}mb-5{% else %}border border-gray-200 mb-8 rounded-md pt-3 px-3 shadow-sm dark:border-gray-800{% endif %}">
17
17
  {% for line in fieldset %}
18
- <div class="form-row block {% if not line.fields|length_is:'1' %}flex flex-row flex-wrap gap-x-8{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}">
19
- {% for field in line %}
20
- <div class="flex group {% if field.errors %}errors {% endif %}{% if not line.fields|length_is:1 %}lg:max-w-xs w-full {% endif %}{% if not forloop.parentloop.last %} mb-6 {% else %} mb-3{% endif %} {% if row %}flex-row items-center{% else %}flex-col{% endif %}">
21
- {% if field.is_checkbox %}
22
- <div class="flex flex-row items-center">
23
- {{ field.field }}{{ field.label_tag }}
24
- </div>
25
- {% else %}
26
- <div class="{% if row %} w-48{% endif %}">
27
- {{ field.label_tag }}
28
- </div>
29
-
30
- {% if field.is_readonly %}
31
- <div class="readonly bg-gray-50 border font-medium max-w-2xl px-3 py-2 rounded-md shadow-sm text-gray-500 text-sm dark:border-gray-700 dark:text-gray-400 dark:bg-gray-800 {% if field.is_json %}truncate whitespace-pre-wrap{% endif %}">{% if field.contents %}{{ field.contents }}{% else %}-{% endif %}</div>
32
- {% else %}
33
- {{ field.field }}
34
- {% endif %}
35
- {% endif %}
36
-
37
- {% if field.errors %}
38
- <span class="mt-1 text-red-600 text-sm dark:text-red-500">
39
- {{ field.errors }}
40
- </span>
41
- {% endif %}
42
-
43
- {% if field.field.help_text %}
44
- {% include "unfold/helpers/help_text.html" with help_text=field.field.help_text %}
45
- {% endif %}
46
- </div>
47
- {% endfor %}
48
- </div>
18
+ {% include "unfold/helpers/fieldset_row.html" %}
49
19
  {% endfor %}
50
20
  </div>
51
21
  </fieldset>
@@ -1,7 +1,7 @@
1
1
  {% load i18n %}
2
2
 
3
3
  {% if sidebar_navigation %}
4
- <div class="overflow-auto">
4
+ <div class="h-0 flex-grow overflow-auto" data-simplebar>
5
5
  {% for group in sidebar_navigation %}
6
6
  {% if group.items %}
7
7
  {% if group.separator %}
@@ -1,20 +1,23 @@
1
- <div class="flex gap-4 items-center">
1
+ <span class="flex gap-4 items-center">
2
2
  {% if value.3 and value.3.path %}
3
- <div class="bg-center bg-cover bg-white border flex font-medium h-8 w-8 dark:bg-gray-900 dark:border-gray-700 {% if value.3.squared %}rounded-sm{% else %}rounded-full{% endif %}" style="background-image: url('{{ value.3.path }}')">
4
- </div>
3
+ <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 %}">
4
+ <img loading="lazy" src="{{ value.3.path }}" class="object-cover" {% if value.3.width %}width="{{ value.3.width }}"{% endif %} {% if value.3.height %}height="{{ value.3.height }}"{% endif %}/>
5
+ </span>
5
6
  {% elif value.2 %}
6
- <div 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">
7
+ <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">
7
8
  {{ value.2 }}
8
- </div>
9
+ </span>
9
10
  {% endif %}
10
11
 
11
- <div class="flex flex-col text-right lg:text-left">
12
- <h3>{{ value.0 }}</h3>
12
+ <span class="flex flex-col text-right lg:text-left">
13
+ {% if value.0 %}
14
+ <h3>{{ value.0 }}</h3>
15
+ {% endif %}
13
16
 
14
17
  {% if value.1 %}
15
18
  <p class="text-gray-500">
16
19
  {{ value.1 }}
17
20
  </p>
18
21
  {% endif %}
19
- </div>
22
+ </span>
20
23
  </div>
@@ -1,9 +1,23 @@
1
- <div class="{% if field.errors %}errors {% endif %}flex group mb-6 flex-col last:mb-4">
2
- {% include "unfold/helpers/form_label.html" with field=field %}
1
+ {% if field.field.widget.input_type == "checkbox" %}
2
+ <div class="{% if field.errors %}errors {% endif %}flex flex-col group mb-6 last:mb-4">
3
+ <div class="flex flex-row gap-2 items-center">
4
+ {{ field }}
3
5
 
4
- {{ field }}
6
+ {% include "unfold/helpers/form_label.html" with field=field %}
7
+ </div>
5
8
 
6
- {% include "unfold/helpers/form_errors.html" with errors=field.errors %}
9
+ {% include "unfold/helpers/form_errors.html" with errors=field.errors %}
7
10
 
8
- {% include "unfold/helpers/help_text.html" with help_text=field.help_text %}
9
- </div>
11
+ {% include "unfold/helpers/help_text.html" with help_text=field.help_text %}
12
+ </div>
13
+ {% else %}
14
+ <div class="{% if field.errors %}errors {% endif %}flex flex-col group mb-6 last:mb-4">
15
+ {% include "unfold/helpers/form_label.html" with field=field %}
16
+
17
+ {{ field }}
18
+
19
+ {% include "unfold/helpers/form_errors.html" with errors=field.errors %}
20
+
21
+ {% include "unfold/helpers/help_text.html" with help_text=field.help_text %}
22
+ </div>
23
+ {% endif %}
@@ -3,7 +3,5 @@
3
3
  {{ title }}
4
4
  </label>
5
5
 
6
- <div class="readonly bg-gray-50 border font-medium max-w-2xl px-3 py-2 rounded-md shadow-sm text-gray-500 text-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400">
7
- {{ value }}
8
- </div>
6
+ {% include "unfold/helpers/field_readonly_value.html" %}
9
7
  </div>
@@ -0,0 +1 @@
1
+ <div class="readonly font-medium max-w-2xl py-2 text-gray-500 text-sm dark:text-gray-400 *:rounded-md {% if not adminform.model_admin.compressed_fields or tabular and not field.is_image %}bg-gray-50 border px-3 rounded-md shadow-sm dark:border-gray-700 dark:bg-gray-800{% endif %} {% if field.is_image %}inline-block{% endif %} {% if field.is_json %}truncate whitespace-pre-wrap{% endif %}">{% if value %}{{ value }}{% elif field.contents %}{{ field.contents }}{% else %}-{% endif %}</div>
@@ -0,0 +1,53 @@
1
+ <div class="form-row
2
+ {% if adminform.model_admin.compressed_fields %} border-b border-gray-200 -mx-3 px-3 pt-3 first:pt-0 last:border-b-0{% endif %}
3
+ {% if not line.fields|length == 1 %} flex flex-row flex-wrap gap-x-8{% endif %}
4
+ {% if not line.has_visible_field %} hidden{% endif %}
5
+ {% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}">
6
+ {% for field in line %}
7
+ <div class="flex group {% if not line.fields|length == 1 and not adminform.model_admin.compressed_fields %} lg:max-w-xs flex-grow{% endif %}{% if field.errors %} errors {% endif %}{% if not forloop.parentloop.last %} {% if adminform.model_admin.compressed_fields %}mb-3{% else %}mb-6{% endif %}{% else %} pb-3{% endif %} {% if adminform.model_admin.compressed_fields %}flex-col lg:flex-row lg:gap-2 {% else %}flex-col{% endif %}">
8
+ {% if field.is_checkbox %}
9
+ <div class="flex flex-row">
10
+ {{ field.field }}
11
+
12
+ <div class="flex flex-col">
13
+ {{ field.label_tag }}
14
+
15
+ {% if field.field.help_text %}
16
+ <div class="ml-2 -mt-1">
17
+ {% include "unfold/helpers/help_text.html" with help_text=field.field.help_text %}
18
+ </div>
19
+ {% endif %}
20
+
21
+ {% if field.errors %}
22
+ <span class="mt-1 text-red-600 text-sm dark:text-red-500">
23
+ {{ field.errors }}
24
+ </span>
25
+ {% endif %}
26
+ </div>
27
+ </div>
28
+ {% else %}
29
+ <div class="{% if adminform.model_admin.compressed_fields %} min-w-48 mt-2 w-48{% endif %}">
30
+ {{ field.label_tag }}
31
+ </div>
32
+
33
+ <div class="flex-grow">
34
+ {% if field.is_readonly %}
35
+ {% include "unfold/helpers/field_readonly_value.html" %}
36
+ {% else %}
37
+ {{ field.field }}
38
+ {% endif %}
39
+
40
+ {% if field.field.help_text and not field.is_checkbox %}
41
+ {% include "unfold/helpers/help_text.html" with help_text=field.field.help_text %}
42
+ {% endif %}
43
+
44
+ {% if field.errors %}
45
+ <span class="mt-1 text-red-600 text-sm dark:text-red-500">
46
+ {{ field.errors }}
47
+ </span>
48
+ {% endif %}
49
+ </div>
50
+ {% endif %}
51
+ </div>
52
+ {% endfor %}
53
+ </div>
@@ -7,8 +7,8 @@
7
7
  {% for fieldset in tabs %}
8
8
  <li>
9
9
  <a class="cursor-pointer font-semibold hover:text-gray-700 dark:hover:text-white"
10
- x-on:click="openTab = '{{ fieldset.name|slugify }}'"
11
- x-bind:class="openTab == '{{ fieldset.name|slugify }}'{% if forloop.first %} || openTab == null{% endif %} ? 'text-gray-700 dark:text-white' : ''">
10
+ x-on:click="openTab = '{{ forloop.counter0 }}-{{ fieldset.name|slugify }}'"
11
+ x-bind:class="openTab == '{{ forloop.counter0 }}-{{ fieldset.name|slugify }}'{% if forloop.first %} || openTab == null{% endif %} ? 'text-gray-700 dark:text-white' : ''">
12
12
  {{ fieldset.name }}
13
13
  </a>
14
14
  </li>
@@ -16,8 +16,8 @@
16
16
  </ul>
17
17
 
18
18
  {% for fieldset in tabs %}
19
- <div class="tab-wrapper{% if fieldset.name %} fieldset-{{ fieldset.name|slugify }}{% endif %}"
20
- x-show="openTab == '{{ fieldset.name|slugify }}'{% if forloop.first %} || openTab == null{% endif %}">
19
+ <div class="tab-wrapper{% if fieldset.name %} fieldset-{{ fieldset.name|slugify }} fieldset-{{ forloop.counter0 }}-{{ fieldset.name|slugify }}{% endif %}"
20
+ x-show="openTab == '{{ forloop.counter0 }}-{{ fieldset.name|slugify }}'{% if forloop.first %} || openTab == null{% endif %}">
21
21
  {% include 'admin/includes/fieldset.html' %}
22
22
  </div>
23
23
  {% endfor %}
@@ -1,4 +1,4 @@
1
- <label for="{{ field.id_for_label }}" class="block font-medium mb-2 text-gray-900 text-sm dark:text-gray-200">
1
+ <label for="{{ field.id_for_label }}" class="block text-gray-900 text-sm dark:text-gray-200{% if field.field.widget.input_type == "checkbox" %}{% else %} font-medium mb-2{% endif %}">
2
2
  {{ field.label }}
3
3
 
4
4
  {% if field.field.required %}
@@ -25,6 +25,7 @@
25
25
  {% endfor %}
26
26
 
27
27
  <link href="{% static 'unfold/css/styles.css' %}" rel="stylesheet">
28
+ <link href="{% static 'unfold/css/simplebar.css' %}" rel="stylesheet">
28
29
 
29
30
  <script src="{% static 'unfold/js/alpine.persist.js' %}" defer></script>
30
31
  <script src="{% static 'unfold/js/alpine.js' %}" defer></script>
@@ -66,6 +67,7 @@
66
67
  {% block base %}{% endblock %}
67
68
 
68
69
  <div id="modal-overlay" class="backdrop-blur-sm bg-opacity-80 bg-gray-900 bottom-0 fixed hidden left-0 mr-1 right-0 top-0 z-50"></div>
70
+ <script src="{% static 'unfold/js/simplebar.js' %}"></script>
69
71
  </body>
70
72
 
71
73
  </html>
@@ -1,7 +1,7 @@
1
1
  {% load i18n %}
2
2
 
3
3
  {% if widget.attrs.accept == 'image/*' and widget.is_initial %}
4
- <div class="bg-gray-50 border flex items-center justify-center mb-4 min-h-32 p-1 rounded-md w-48 dark:bg-white/[.02] dark:border-gray-700">
4
+ <div class="mb-4 max-w-48">
5
5
  <a href="{{ widget.value.url }}" target="_blank">
6
6
  <img src="{{ widget.value.url }}" alt="{% trans 'Image preview' %}" class="block rounded" />
7
7
  </a>
@@ -0,0 +1,15 @@
1
+ <div class="flex flex-row">
2
+ {% include 'django/forms/widgets/input.html' %}
3
+
4
+ {% if related_url %}
5
+ <a href="{{ related_url }}" class="related-lookup 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" id="lookup_id_{{ widget.name }}">
6
+ <span class="material-symbols-outlined text-sm">search</span>
7
+ </a>
8
+ {% endif %}
9
+
10
+ {% if link_label and link_url %}
11
+ <a href="{{ link_url }}" title="{{ link_label }}" class="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">
12
+ <span class="material-symbols-outlined text-sm">visibility</span>
13
+ </a>
14
+ {% endif %}
15
+ </div>
@@ -1,7 +1 @@
1
- <div class="relative">
2
- <div class="border break-words font-medium invisible max-w-4xl px-3 py-2 text-sm" style="min-height: 64px">
3
- {% if widget.value %}{{ widget.value|linebreaks }}{% endif %}
4
- </div>
5
-
6
- <textarea onInput="this.previousElementSibling.innerText = this.value + String.fromCharCode(10)" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
7
- </div>
1
+ <textarea name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
@@ -0,0 +1,7 @@
1
+ <div class="relative">
2
+ <div class="border break-words font-medium invisible max-w-4xl px-3 py-2 text-sm" style="min-height: 64px">
3
+ {% if widget.value %}{{ widget.value|linebreaks }}{% endif %}
4
+ </div>
5
+
6
+ <textarea onInput="this.previousElementSibling.innerText = this.value + String.fromCharCode(10)" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>{% if widget.value %}{{ widget.value }}{% endif %}</textarea>
7
+ </div>
unfold/widgets.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Callable, Dict, Optional, Tuple, Union
2
2
 
3
3
  from django.contrib.admin.options import VERTICAL
4
+ from django.contrib.admin.sites import AdminSite
4
5
  from django.contrib.admin.widgets import (
5
6
  AdminBigIntegerFieldWidget,
6
7
  AdminDateWidget,
@@ -13,8 +14,10 @@ from django.contrib.admin.widgets import (
13
14
  AdminTextInputWidget,
14
15
  AdminTimeWidget,
15
16
  AdminUUIDInputWidget,
17
+ ForeignKeyRawIdWidget,
16
18
  RelatedFieldWidgetWrapper,
17
19
  )
20
+ from django.db.models.fields.reverse_related import ForeignObjectRel
18
21
  from django.forms import (
19
22
  CheckboxInput,
20
23
  MultiWidget,
@@ -106,7 +109,6 @@ SELECT_CLASSES = [
106
109
  "pr-8",
107
110
  "max-w-2xl",
108
111
  "appearance-none",
109
- "truncate",
110
112
  ]
111
113
 
112
114
  PROSE_CLASSES = [
@@ -358,6 +360,20 @@ class UnfoldAdminSingleTimeWidget(AdminTimeWidget):
358
360
  class UnfoldAdminTextareaWidget(AdminTextareaWidget):
359
361
  template_name = "unfold/widgets/textarea.html"
360
362
 
363
+ def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
364
+ attrs = attrs or {}
365
+
366
+ super().__init__(
367
+ attrs={
368
+ "class": "vLargeTextField " + " ".join(TEXTAREA_CLASSES),
369
+ **(attrs or {}),
370
+ }
371
+ )
372
+
373
+
374
+ class UnfoldAdminExpandableTextareaWidget(AdminTextareaWidget):
375
+ template_name = "unfold/widgets/textarea_expandable.html"
376
+
361
377
  def __init__(self, attrs: Optional[Dict[str, Any]] = None) -> None:
362
378
  attrs = attrs or {}
363
379
 
@@ -499,7 +515,7 @@ class UnfoldBooleanWidget(CheckboxInput):
499
515
  if attrs is None:
500
516
  attrs = {}
501
517
 
502
- return super().__init__(
518
+ super().__init__(
503
519
  {
504
520
  **(attrs or {}),
505
521
  "class": " ".join(CHECKBOX_CLASSES + [attrs.get("class", "")]),
@@ -512,10 +528,27 @@ class UnfoldBooleanSwitchWidget(CheckboxInput):
512
528
  def __init__(
513
529
  self, attrs: Optional[Dict[str, Any]] = None, check_test: Callable = None
514
530
  ) -> None:
515
- return super().__init__(
531
+ super().__init__(
516
532
  attrs={"class": " ".join(SWITCH_CLASSES), **(attrs or {})}, check_test=None
517
533
  )
518
534
 
519
535
 
520
536
  class UnfoldRelatedFieldWidgetWrapper(RelatedFieldWidgetWrapper):
521
537
  template_name = "unfold/widgets/related_widget_wrapper.html"
538
+
539
+
540
+ class UnfoldForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
541
+ template_name = "unfold/widgets/foreign_key_raw_id.html"
542
+
543
+ def __init__(
544
+ self,
545
+ rel: ForeignObjectRel,
546
+ admin_site: AdminSite,
547
+ attrs: Optional[Dict] = None,
548
+ using: Optional[Any] = None,
549
+ ) -> None:
550
+ attrs = {
551
+ "class": " ".join(["vForeignKeyRawIdAdminField"] + INPUT_CLASSES),
552
+ **(attrs or {}),
553
+ }
554
+ super().__init__(rel, admin_site, attrs, using)
@@ -1,37 +0,0 @@
1
- from django import forms
2
- from django.utils.translation import gettext_lazy as _
3
- from import_export.admin import ExportActionModelAdmin as BaseExportActionModelAdmin
4
- from unfold.admin import ActionForm
5
- from unfold.widgets import SELECT_CLASSES
6
-
7
-
8
- def export_action_form_factory(formats):
9
- class _ExportActionForm(ActionForm):
10
- format = forms.ChoiceField(
11
- label=" ",
12
- choices=formats,
13
- required=False,
14
- widget=forms.Select(
15
- {"class": " ".join([*SELECT_CLASSES, "ml-3", "!w-auto", "lg:!w-40"])}
16
- ),
17
- )
18
-
19
- _ExportActionForm.__name__ = "ExportActionForm"
20
-
21
- return _ExportActionForm
22
-
23
-
24
- class ExportActionModelAdmin(BaseExportActionModelAdmin):
25
- def __init__(self, *args, **kwargs):
26
- super().__init__(*args, **kwargs)
27
-
28
- choices = []
29
- formats = self.get_export_formats()
30
- if formats:
31
- for i, f in enumerate(formats):
32
- choices.append((str(i), f().get_title()))
33
-
34
- if len(formats) > 1:
35
- choices.insert(0, ("", _("Select format")))
36
-
37
- self.action_form = export_action_form_factory(choices)