django-unfold 0.30.0__py3-none-any.whl → 0.31.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- {django_unfold-0.30.0.dist-info → django_unfold-0.31.0.dist-info}/METADATA +59 -18
- {django_unfold-0.30.0.dist-info → django_unfold-0.31.0.dist-info}/RECORD +72 -71
- unfold/admin.py +30 -11
- unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +3 -3
- unfold/contrib/forms/templates/unfold/forms/array.html +3 -1
- unfold/contrib/forms/templates/unfold/forms/helpers/toolbar.html +6 -6
- unfold/contrib/forms/templates/unfold/forms/wysiwyg.html +1 -1
- unfold/contrib/forms/widgets.py +22 -11
- unfold/contrib/guardian/templates/unfold/guardian/group_form.html +4 -4
- unfold/contrib/guardian/templates/unfold/guardian/user_form.html +4 -4
- unfold/contrib/import_export/templates/admin/import_export/change_form.html +1 -1
- unfold/contrib/import_export/templates/admin/import_export/import_errors.html +1 -1
- unfold/contrib/import_export/templates/admin/import_export/import_preview.html +3 -3
- unfold/contrib/import_export/templates/admin/import_export/import_validation.html +4 -4
- unfold/contrib/inlines/forms.py +1 -2
- unfold/contrib/simple_history/templates/simple_history/object_history_list.html +9 -9
- unfold/contrib/simple_history/templates/simple_history/submit_line.html +1 -1
- unfold/dataclasses.py +10 -1
- unfold/fields.py +1 -1
- unfold/settings.py +1 -0
- unfold/sites.py +39 -15
- unfold/static/unfold/css/styles.css +1 -1
- unfold/static/unfold/js/alpine.anchor.js +1 -0
- unfold/static/unfold/js/alpine.js +2 -2
- unfold/static/unfold/js/alpine.persist.js +1 -1
- unfold/static/unfold/js/app.js +26 -3
- unfold/styles.css +10 -10
- unfold/templates/admin/actions.html +1 -1
- unfold/templates/admin/app_list.html +1 -1
- unfold/templates/admin/base.html +4 -4
- unfold/templates/admin/change_list.html +2 -2
- unfold/templates/admin/change_list_results.html +2 -2
- unfold/templates/admin/delete_confirmation.html +4 -4
- unfold/templates/admin/delete_selected_confirmation.html +4 -4
- unfold/templates/admin/edit_inline/stacked.html +2 -2
- unfold/templates/admin/edit_inline/tabular.html +3 -3
- unfold/templates/admin/filter.html +2 -2
- unfold/templates/admin/includes/fieldset.html +1 -1
- unfold/templates/admin/includes/object_delete_summary.html +1 -1
- unfold/templates/admin/login.html +8 -8
- unfold/templates/admin/object_history.html +4 -4
- unfold/templates/admin/search_form.html +1 -1
- unfold/templates/admin/submit_line.html +7 -5
- unfold/templates/auth/widgets/read_only_password_hash.html +1 -1
- unfold/templates/registration/logged_out.html +1 -1
- unfold/templates/unfold/change_list_filter.html +9 -1
- unfold/templates/unfold/helpers/account_links.html +2 -2
- unfold/templates/unfold/helpers/actions_row.html +4 -4
- unfold/templates/unfold/helpers/app_list.html +48 -38
- unfold/templates/unfold/helpers/app_list_default.html +4 -4
- unfold/templates/unfold/helpers/breadcrumb_item.html +1 -1
- unfold/templates/unfold/helpers/field_readonly_value.html +1 -1
- unfold/templates/unfold/helpers/fieldset_row.html +6 -6
- unfold/templates/unfold/helpers/fieldsets_tabs.html +2 -2
- unfold/templates/unfold/helpers/header.html +1 -1
- unfold/templates/unfold/helpers/help_text.html +1 -1
- unfold/templates/unfold/helpers/history.html +1 -1
- unfold/templates/unfold/helpers/label.html +1 -1
- unfold/templates/unfold/helpers/search.html +7 -4
- unfold/templates/unfold/helpers/search_results.html +2 -2
- unfold/templates/unfold/helpers/tab_action.html +1 -1
- unfold/templates/unfold/helpers/tab_list.html +27 -5
- unfold/templates/unfold/helpers/theme_switch.html +2 -2
- unfold/templates/unfold/layouts/skeleton.html +6 -1
- unfold/templates/unfold/widgets/clearable_file_input.html +14 -6
- unfold/templates/unfold/widgets/clearable_file_input_small.html +4 -4
- unfold/templates/unfold/widgets/split_datetime.html +2 -2
- unfold/templatetags/unfold.py +33 -12
- unfold/templatetags/unfold_list.py +16 -6
- unfold/widgets.py +2 -2
- {django_unfold-0.30.0.dist-info → django_unfold-0.31.0.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.30.0.dist-info → django_unfold-0.31.0.dist-info}/WHEEL +0 -0
@@ -9,11 +9,11 @@
|
|
9
9
|
|
10
10
|
{% if choice.min is not None and choice.max is not None and choice.step %}
|
11
11
|
<div class="admin-numeric-filter-slider-tooltips">
|
12
|
-
<span class="admin-numeric-filter-slider-tooltip-from border cursor-not-allowed flex flex-grow flex-row items-center mr-auto rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
12
|
+
<span class="admin-numeric-filter-slider-tooltip-from border cursor-not-allowed flex flex-grow flex-row items-center mr-auto rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
13
13
|
{{ choice.value_from }}
|
14
14
|
</span>
|
15
15
|
|
16
|
-
<span class="admin-numeric-filter-slider-tooltip-to border cursor-not-allowed flex flex-grow flex-row items-center rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
16
|
+
<span class="admin-numeric-filter-slider-tooltip-to border cursor-not-allowed flex flex-grow flex-row items-center rounded-md shadow-sm px-3 py-2 w-full dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
17
17
|
{{ choice.value_to }}
|
18
18
|
</span>
|
19
19
|
</div>
|
@@ -25,7 +25,7 @@
|
|
25
25
|
{{ choice.form.as_p }}
|
26
26
|
</div>
|
27
27
|
{% else %}
|
28
|
-
<div class="admin-numeric-filter-slider-error dark:text-gray-
|
28
|
+
<div class="admin-numeric-filter-slider-error dark:text-gray-300">
|
29
29
|
<p>
|
30
30
|
{% trans 'Not enough data.' %}
|
31
31
|
</p>
|
@@ -15,7 +15,9 @@
|
|
15
15
|
|
16
16
|
<template x-for="(item, index) in items" :key="item.key">
|
17
17
|
<div class="flex flex-row">
|
18
|
-
{%
|
18
|
+
{% with widget=template %}
|
19
|
+
{% include template.template_name %}
|
20
|
+
{% endwith %}
|
19
21
|
|
20
22
|
<a x-on:click="items.splice(index, 1)" class="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">
|
21
23
|
<span class="material-symbols-outlined text-sm">delete</span>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
<template id="trix-toolbar">
|
4
4
|
<div class="bg-gray-50 flex flex-row flex-wrap border group-[.errors]:border-t-red-600 gap-4 group-[.errors]:border-x-red-600 px-3 py-2 relative rounded-t-md shadow-sm w-full dark:bg-white/[.02] dark:border-gray-700 dark:group-[.errors]:border-t-red-500 dark:group-[.errors]:border-x-red-500">
|
5
|
-
<div data-trix-button-group="text-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm shrink-0 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
5
|
+
<div data-trix-button-group="text-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm shrink-0 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
6
6
|
{% spaceless %}
|
7
7
|
<button type="button" data-trix-attribute="p" data-trix-key="p" title="{% trans "Paragraph" %}" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
8
8
|
<span class="material-symbols-outlined">format_paragraph</span>
|
@@ -30,7 +30,7 @@
|
|
30
30
|
{% endspaceless %}
|
31
31
|
</div>
|
32
32
|
|
33
|
-
<div data-trix-button-group="block-headings" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
33
|
+
<div data-trix-button-group="block-headings" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
34
34
|
{% spaceless %}
|
35
35
|
<button type="button" data-trix-attribute="heading1" title="{% trans "Heading" %} 1" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
36
36
|
<span class="material-symbols-outlined">format_h1</span>
|
@@ -50,7 +50,7 @@
|
|
50
50
|
{% endspaceless %}
|
51
51
|
</div>
|
52
52
|
|
53
|
-
<div data-trix-button-group="block-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
53
|
+
<div data-trix-button-group="block-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
54
54
|
{% spaceless %}
|
55
55
|
<button type="button" data-trix-attribute="quote" title="{% trans "Quote" %}" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
56
56
|
<span class="material-symbols-outlined">format_quote</span>
|
@@ -79,7 +79,7 @@
|
|
79
79
|
</div>
|
80
80
|
|
81
81
|
<div class="lg:ml-auto">
|
82
|
-
<div data-trix-button-group="history-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
82
|
+
<div data-trix-button-group="history-tools" class="bg-white border border-md flex flex-row rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
83
83
|
<button type="button" data-trix-action="undo" data-trix-key="z" title="{% trans "Undo" %}" tabindex="-1" class="border-r cursor-pointer flex items-center h-8 justify-center transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
84
84
|
<span class="material-symbols-outlined">undo</span>
|
85
85
|
</button>
|
@@ -92,9 +92,9 @@
|
|
92
92
|
<div data-trix-dialogs>
|
93
93
|
<div class="absolute bg-white border-b -bottom-px left-0 px-4 py-2 right-0 shadow-sm translate-y-full dark:bg-gray-900 dark:border-gray-700" data-trix-dialog="href" data-trix-dialog-attribute="href">
|
94
94
|
<div class="flex flex-row">
|
95
|
-
<input type="url" name="href" class="border bg-white font-medium px-3 rounded-md shadow-sm text-gray-500 text-sm focus:ring focus:ring-primary-300 focus:border-primary-600 focus:outline-none group-[.errors]:border-red-600 group-[.errors]:focus:ring-red-200 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
95
|
+
<input type="url" name="href" class="border bg-white font-medium px-3 rounded-md shadow-sm text-gray-500 text-sm focus:ring focus:ring-primary-300 focus:border-primary-600 focus:outline-none group-[.errors]:border-red-600 group-[.errors]:focus:ring-red-200 dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300 dark:focus:ring-primary-600/30 dark:group-[.errors]:border-red-500 dark:group-[.errors]:focus:ring-red-600/40" placeholder="{% trans "Enter an URL" %}" required data-trix-input>
|
96
96
|
|
97
|
-
<div class="bg-white border border-md flex flex-row ml-4 rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-
|
97
|
+
<div class="bg-white border border-md flex flex-row ml-4 rounded-md shadow-sm dark:bg-gray-900 dark:border-gray-700 dark:text-gray-300">
|
98
98
|
<button type="button" data-trix-method="setAttribute" title="{% trans "Link" %}" class="border-r cursor-pointer flex items-center h-8 justify-center text-gray-400 transition-colors w-8 hover:text-primary-600 dark:border-gray-700">
|
99
99
|
<span class="material-symbols-outlined">link</span>
|
100
100
|
</button>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
{% include "unfold/forms/helpers/toolbar.html" %}
|
2
2
|
|
3
|
-
<input type="hidden" name="{{ widget.name }}" id="wysiwyg-{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value }}"{% endif %}>
|
3
|
+
<input type="hidden" name="{{ widget.name }}" id="wysiwyg-{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value }}"{% endif %} {% include "django/forms/widgets/attrs.html" %}>
|
4
4
|
|
5
5
|
<div class="max-w-4xl relative">
|
6
6
|
<trix-editor input="wysiwyg-{{ widget.name }}" {% include "django/forms/widgets/attrs.html" %}></trix-editor>
|
unfold/contrib/forms/widgets.py
CHANGED
@@ -4,7 +4,11 @@ from django.core.validators import EMPTY_VALUES
|
|
4
4
|
from django.forms import MultiWidget, Widget
|
5
5
|
from django.http import QueryDict
|
6
6
|
from django.utils.datastructures import MultiValueDict
|
7
|
-
from unfold.widgets import
|
7
|
+
from unfold.widgets import (
|
8
|
+
PROSE_CLASSES,
|
9
|
+
UnfoldAdminSelectWidget,
|
10
|
+
UnfoldAdminTextInputWidget,
|
11
|
+
)
|
8
12
|
|
9
13
|
WYSIWYG_CLASSES = [
|
10
14
|
*PROSE_CLASSES,
|
@@ -20,26 +24,33 @@ WYSIWYG_CLASSES = [
|
|
20
24
|
"w-full",
|
21
25
|
"focus:outline-none",
|
22
26
|
"dark:border-gray-700",
|
23
|
-
"dark:text-gray-
|
27
|
+
"dark:text-gray-300",
|
24
28
|
"dark:group-[.errors]:border-red-500",
|
25
29
|
]
|
26
30
|
|
27
31
|
|
28
32
|
class ArrayWidget(MultiWidget):
|
29
33
|
template_name = "unfold/forms/array.html"
|
30
|
-
widget_class = UnfoldAdminTextInputWidget
|
31
34
|
|
32
35
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
33
|
-
|
36
|
+
if "choices" in kwargs:
|
37
|
+
self.choices = kwargs["choices"]
|
38
|
+
|
39
|
+
widgets = [self.get_widget_instance()]
|
34
40
|
super().__init__(widgets)
|
35
41
|
|
42
|
+
def get_widget_instance(self) -> Any:
|
43
|
+
if hasattr(self, "choices"):
|
44
|
+
return UnfoldAdminSelectWidget(choices=self.choices)
|
45
|
+
|
46
|
+
return UnfoldAdminTextInputWidget()
|
47
|
+
|
36
48
|
def get_context(self, name: str, value: str, attrs: Dict) -> Dict:
|
37
49
|
self._resolve_widgets(value)
|
38
50
|
context = super().get_context(name, value, attrs)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
context.update({"template": template_widget})
|
51
|
+
context.update(
|
52
|
+
{"template": self.get_widget_instance().get_context(name, "", {})["widget"]}
|
53
|
+
)
|
43
54
|
return context
|
44
55
|
|
45
56
|
def value_from_datadict(
|
@@ -71,12 +82,12 @@ class ArrayWidget(MultiWidget):
|
|
71
82
|
value = []
|
72
83
|
|
73
84
|
elif isinstance(value, List):
|
74
|
-
self.widgets = [self.
|
85
|
+
self.widgets = [self.get_widget_instance() for item in value]
|
75
86
|
else:
|
76
|
-
self.widgets = [self.
|
87
|
+
self.widgets = [self.get_widget_instance() for item in value.split(",")]
|
77
88
|
|
78
89
|
self.widgets_names = ["" for i in range(len(self.widgets))]
|
79
|
-
self.widgets = [w
|
90
|
+
self.widgets = [w if isinstance(w, type) else w for w in self.widgets]
|
80
91
|
|
81
92
|
|
82
93
|
class WysiwygWidget(Widget):
|
@@ -8,7 +8,7 @@
|
|
8
8
|
</h2>
|
9
9
|
|
10
10
|
{% if groups_perms.items %}
|
11
|
-
<table id="group-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-
|
11
|
+
<table id="group-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
12
12
|
<thead class="hidden lg:table-header-group">
|
13
13
|
<tr>
|
14
14
|
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -30,14 +30,14 @@
|
|
30
30
|
<tbody>
|
31
31
|
{% for group, group_perms in groups_perms.items %}
|
32
32
|
<tr class="block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
33
|
-
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
33
|
+
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "User" %}">
|
34
34
|
<span class="text-gray-700 dark:text-gray-200">
|
35
35
|
{{ group }}
|
36
36
|
</span>
|
37
37
|
</th>
|
38
38
|
|
39
39
|
{% for perm in model_perms %}
|
40
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
40
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{{ perm.name }}">
|
41
41
|
{% if perm.codename in group_perms %}
|
42
42
|
{% include "unfold/helpers/boolean.html" with value=False %}
|
43
43
|
{% else %}
|
@@ -46,7 +46,7 @@
|
|
46
46
|
</td>
|
47
47
|
{% endfor %}
|
48
48
|
|
49
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
49
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "Action" %}">
|
50
50
|
<a href="group-manage/{{ group.id|safe }}/" class="hover:text-gray-700 dark:hover:text-white">
|
51
51
|
{% trans "Edit" %}
|
52
52
|
</a>
|
@@ -8,7 +8,7 @@
|
|
8
8
|
</h2>
|
9
9
|
|
10
10
|
{% if users_perms.items %}
|
11
|
-
<table id="user-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-
|
11
|
+
<table id="user-permissions" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
12
12
|
<thead class="hidden lg:table-header-group">
|
13
13
|
<tr>
|
14
14
|
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -30,14 +30,14 @@
|
|
30
30
|
<tbody>
|
31
31
|
{% for user, user_perms in users_perms.items %}
|
32
32
|
<tr class="block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
33
|
-
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
33
|
+
<th class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "User" %}">
|
34
34
|
<span class="text-gray-700 dark:text-gray-200">
|
35
35
|
{{ user }}
|
36
36
|
</span>
|
37
37
|
</th>
|
38
38
|
|
39
39
|
{% for perm in model_perms %}
|
40
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
40
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{{ perm.name }}">
|
41
41
|
{% if perm.codename in group_perms %}
|
42
42
|
{% include "unfold/helpers/boolean.html" with value=False %}
|
43
43
|
{% else %}
|
@@ -46,7 +46,7 @@
|
|
46
46
|
</td>
|
47
47
|
{% endfor %}
|
48
48
|
|
49
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
49
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-right text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans "Action" %}">
|
50
50
|
<a href="user-manage/{{ user.id|safe }}/" class="hover:text-gray-700 dark:hover:text-white">
|
51
51
|
{% trans "Edit" %}
|
52
52
|
</a>
|
@@ -5,6 +5,6 @@
|
|
5
5
|
{{ block.super }}
|
6
6
|
|
7
7
|
{% if show_change_form_export %}
|
8
|
-
<input type="submit" value="{% translate 'Export' %}" name="_export-item" 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-
|
8
|
+
<input type="submit" value="{% translate 'Export' %}" name="_export-item" 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-300">
|
9
9
|
{% endif %}
|
10
10
|
{% endblock %}
|
@@ -26,7 +26,7 @@
|
|
26
26
|
</div>
|
27
27
|
</div>
|
28
28
|
|
29
|
-
<div class="block border leading-relaxed rounded p-4 text-sm traceback dark:border-gray-800 dark:text-gray-
|
29
|
+
<div class="block border leading-relaxed rounded p-4 text-sm traceback dark:border-gray-800 dark:text-gray-300 " x-show="open">
|
30
30
|
{{ error.traceback|linebreaks }}
|
31
31
|
</div>
|
32
32
|
</li>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{% load admin_urls i18n import_export_tags unfold %}
|
2
2
|
|
3
3
|
{% block preview %}
|
4
|
-
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-
|
4
|
+
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
5
5
|
<thead class="hidden lg:table-header-group">
|
6
6
|
<tr>
|
7
7
|
<th class="align-middle capitalize font-medium px-3 py-2 text-left text-gray-400 text-sm"></th>
|
@@ -17,7 +17,7 @@
|
|
17
17
|
<tbody>
|
18
18
|
{% for row in result.valid_rows %}
|
19
19
|
<tr class="{{ row.import_type }} {% cycle '' 'bg-gray-50 dark:bg-white/[.02]' %} block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
20
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
20
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
21
21
|
{% if row.import_type == 'new' %}
|
22
22
|
{% trans "New" %}
|
23
23
|
{% elif row.import_type == 'skip' %}
|
@@ -30,7 +30,7 @@
|
|
30
30
|
</td>
|
31
31
|
|
32
32
|
{% for field in row.diff %}
|
33
|
-
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
33
|
+
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
34
34
|
{{ field }}
|
35
35
|
</td>
|
36
36
|
{% endfor %}
|
@@ -15,7 +15,7 @@
|
|
15
15
|
</div>
|
16
16
|
</div>
|
17
17
|
|
18
|
-
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-
|
18
|
+
<table class="border-gray-200 border-spacing-none border-separate text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
19
19
|
<thead class="hidden lg:table-header-group">
|
20
20
|
<tr>
|
21
21
|
<th class="align-middle capitalize font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -37,11 +37,11 @@
|
|
37
37
|
<tbody>
|
38
38
|
{% for row in result.invalid_rows %}
|
39
39
|
<tr class="{% cycle '' 'bg-gray-50 dark:bg-white/[.02]' %} block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
40
|
-
<td data-label="{% trans "Row" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
40
|
+
<td data-label="{% trans "Row" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
41
41
|
{{ row.number }}
|
42
42
|
</td>
|
43
43
|
|
44
|
-
<td data-label="{% trans "Errors" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
44
|
+
<td data-label="{% trans "Errors" %}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
45
45
|
<div>
|
46
46
|
<span class="bg-red-600 font-semibold ml-2 px-1 rounded-sm text-xs text-white">{{ row.error_count }}</span>
|
47
47
|
</div>
|
@@ -77,7 +77,7 @@
|
|
77
77
|
</td>
|
78
78
|
|
79
79
|
{% for field in row.values %}
|
80
|
-
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-
|
80
|
+
<td data-label="{{ result.diff_headers|index:forloop.counter0 }}" class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:block before:capitalize before:content-[attr(data-label)] before:mr-auto before:text-gray-500 dark:before:text-gray-300 lg:before:hidden lg:py-3 lg:table-cell dark:border-gray-800">
|
81
81
|
{{ field }}
|
82
82
|
</td>
|
83
83
|
{% endfor %}
|
unfold/contrib/inlines/forms.py
CHANGED
@@ -12,9 +12,8 @@ class NonrelatedInlineModelFormSet(BaseModelFormSet):
|
|
12
12
|
**kwargs: Any,
|
13
13
|
) -> None:
|
14
14
|
self.instance = instance
|
15
|
-
self.queryset = self.provided_queryset
|
16
|
-
|
17
15
|
super().__init__(**kwargs)
|
16
|
+
self.queryset = self.provided_queryset
|
18
17
|
|
19
18
|
@classmethod
|
20
19
|
def get_default_prefix(cls: BaseModelFormSet) -> str:
|
@@ -3,7 +3,7 @@
|
|
3
3
|
{% load admin_urls %}
|
4
4
|
{% load getattribute from getattributes %}
|
5
5
|
|
6
|
-
<table id="change-history" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-
|
6
|
+
<table id="change-history" class="border-gray-200 border-spacing-none border-separate mb-6 text-gray-700 w-full dark:text-gray-300 lg:border lg:rounded-md lg:shadow-sm lg:dark:border-gray-800">
|
7
7
|
<thead class="hidden lg:table-header-group">
|
8
8
|
<tr>
|
9
9
|
<th class="align-middle font-medium px-3 py-2 text-left text-gray-400 text-sm">
|
@@ -41,27 +41,27 @@
|
|
41
41
|
<tbody>
|
42
42
|
{% for record in historical_records %}
|
43
43
|
<tr class="block border mb-3 rounded-md shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-gray-800">
|
44
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
44
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Object' %}">
|
45
45
|
<a href="{% url opts|admin_urlname:'simple_history' object.pk record.pk %}">
|
46
46
|
{{ record.history_object }}
|
47
47
|
</a>
|
48
48
|
</td>
|
49
49
|
|
50
50
|
{% for column in history_list_display %}
|
51
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
51
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans column %}">
|
52
52
|
{{ record|getattribute:column }}
|
53
53
|
</th>
|
54
54
|
{% endfor %}
|
55
55
|
|
56
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
56
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Date/time' %}">
|
57
57
|
{{ record.history_date }}
|
58
58
|
</td>
|
59
59
|
|
60
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
60
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Comment' %}">
|
61
61
|
{{ record.get_history_type_display }}
|
62
62
|
</td>
|
63
63
|
|
64
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
64
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Changed by' %}">
|
65
65
|
{% if record.history_user %}
|
66
66
|
{% url admin_user_view record.history_user_id as admin_user_url %}
|
67
67
|
|
@@ -75,11 +75,11 @@
|
|
75
75
|
{% endif %}
|
76
76
|
</td>
|
77
77
|
|
78
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
78
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Change reason' %}">
|
79
79
|
{{ record.history_change_reason }}
|
80
80
|
</td>
|
81
81
|
|
82
|
-
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-
|
82
|
+
<td class="align-middle flex border-t border-gray-200 font-normal px-3 py-2 text-left text-sm before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto before:text-gray-500 first:border-t-0 dark:before:text-gray-300 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-gray-800" data-label="{% trans 'Changes' %}">
|
83
83
|
{% block history_delta_changes %}
|
84
84
|
{% if record.history_delta_changes %}
|
85
85
|
<ul>
|
@@ -97,7 +97,7 @@
|
|
97
97
|
{% endif %}
|
98
98
|
|
99
99
|
{% if change.old and change.new %}
|
100
|
-
<span class="align-text-top material-symbols-outlined md-18 text-gray-300 group-hover:text-gray-
|
100
|
+
<span class="align-text-top material-symbols-outlined md-18 text-gray-300 group-hover:text-gray-300 dark:text-gray-600">arrow_right_alt</span>
|
101
101
|
{% endif %}
|
102
102
|
|
103
103
|
{% if change.new %}
|
@@ -16,7 +16,7 @@
|
|
16
16
|
</button>
|
17
17
|
{% endif %}
|
18
18
|
|
19
|
-
<a href="{{ history_url }}" class="border font-medium mr-auto px-3 py-2 rounded-md text-sm text-gray-500 text-center transition-all w-full hover:bg-gray-50 lg:block lg:w-auto dark:border-gray-700 dark:text-gray-
|
19
|
+
<a href="{{ history_url }}" class="border font-medium mr-auto px-3 py-2 rounded-md text-sm text-gray-500 text-center transition-all w-full hover:bg-gray-50 lg:block lg:w-auto dark:border-gray-700 dark:text-gray-300 dark:hover:text-gray-200 dark:hover:bg-gray-900">
|
20
20
|
{% trans 'Close' %}
|
21
21
|
</a>
|
22
22
|
</div>
|
unfold/dataclasses.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
|
-
from typing import Dict, Optional
|
2
|
+
from typing import Callable, Dict, Optional, Union
|
3
3
|
|
4
4
|
from .typing import ActionFunction
|
5
5
|
|
@@ -11,3 +11,12 @@ class UnfoldAction:
|
|
11
11
|
description: str
|
12
12
|
path: str
|
13
13
|
attrs: Optional[Dict] = None
|
14
|
+
object_id: Optional[Union[int, str]] = None
|
15
|
+
|
16
|
+
|
17
|
+
@dataclass
|
18
|
+
class Favicon:
|
19
|
+
href: Union[str, Callable]
|
20
|
+
rel: Optional[str] = None
|
21
|
+
type: Optional[str] = None
|
22
|
+
sizes: Optional[str] = None
|
unfold/fields.py
CHANGED
@@ -95,7 +95,7 @@ class UnfoldAdminReadonlyField(helpers.AdminReadonlyField):
|
|
95
95
|
current_app=self.model_admin.admin_site.name,
|
96
96
|
)
|
97
97
|
return format_html(
|
98
|
-
'<a href="{}" class="text-primary-600 underline
|
98
|
+
'<a href="{}" class="text-primary-600 underline dark:text-primary-500">{}</a>',
|
99
99
|
url,
|
100
100
|
remote_obj,
|
101
101
|
)
|
unfold/settings.py
CHANGED
unfold/sites.py
CHANGED
@@ -10,6 +10,7 @@ from django.urls import URLPattern, path, reverse, reverse_lazy
|
|
10
10
|
from django.utils.functional import lazy
|
11
11
|
from django.utils.module_loading import import_string
|
12
12
|
|
13
|
+
from .dataclasses import Favicon
|
13
14
|
from .settings import get_config
|
14
15
|
from .utils import hex_to_rgb
|
15
16
|
from .widgets import CHECKBOX_CLASSES, INPUT_CLASSES
|
@@ -66,6 +67,9 @@ class UnfoldAdminSite(AdminSite):
|
|
66
67
|
"site_symbol": self._get_value(
|
67
68
|
get_config(self.settings_name)["SITE_SYMBOL"], request
|
68
69
|
),
|
70
|
+
"site_favicons": self._process_favicons(
|
71
|
+
request, get_config(self.settings_name)["SITE_FAVICONS"]
|
72
|
+
),
|
69
73
|
"show_history": get_config(self.settings_name)["SHOW_HISTORY"],
|
70
74
|
"show_view_on_site": get_config(self.settings_name)[
|
71
75
|
"SHOW_VIEW_ON_SITE"
|
@@ -220,23 +224,14 @@ class UnfoldAdminSite(AdminSite):
|
|
220
224
|
navigation = get_config(self.settings_name)["SIDEBAR"].get("navigation", [])
|
221
225
|
results = []
|
222
226
|
|
223
|
-
def _get_is_active(link: str) -> bool:
|
224
|
-
if not isinstance(link, str):
|
225
|
-
link = str(link)
|
226
|
-
|
227
|
-
if link in request.path and link != reverse_lazy(f"{self.name}:index"):
|
228
|
-
return True
|
229
|
-
elif link == request.path == reverse_lazy(f"{self.name}:index"):
|
230
|
-
return True
|
231
|
-
|
232
|
-
return False
|
233
|
-
|
234
227
|
for group in navigation:
|
235
228
|
allowed_items = []
|
236
229
|
|
237
230
|
for item in group["items"]:
|
238
231
|
item["active"] = False
|
239
|
-
item["active"] = _get_is_active(
|
232
|
+
item["active"] = self._get_is_active(
|
233
|
+
request, item.get("link_callback") or item["link"]
|
234
|
+
)
|
240
235
|
|
241
236
|
for tab in get_config(self.settings_name)["TABS"]:
|
242
237
|
has_primary_link = False
|
@@ -247,7 +242,9 @@ class UnfoldAdminSite(AdminSite):
|
|
247
242
|
has_primary_link = True
|
248
243
|
continue
|
249
244
|
|
250
|
-
if _get_is_active(
|
245
|
+
if self._get_is_active(
|
246
|
+
request, tab_item.get("link_callback") or tab_item["link"]
|
247
|
+
):
|
251
248
|
has_tab_link_active = True
|
252
249
|
break
|
253
250
|
|
@@ -255,7 +252,7 @@ class UnfoldAdminSite(AdminSite):
|
|
255
252
|
item["active"] = True
|
256
253
|
|
257
254
|
if isinstance(item["link"], Callable):
|
258
|
-
item["
|
255
|
+
item["link_callback"] = lazy(item["link"])(request)
|
259
256
|
|
260
257
|
# Permission callback
|
261
258
|
item["has_permission"] = self._call_permission_callback(
|
@@ -290,8 +287,11 @@ class UnfoldAdminSite(AdminSite):
|
|
290
287
|
)
|
291
288
|
|
292
289
|
if isinstance(item["link"], Callable):
|
293
|
-
item["
|
290
|
+
item["link_callback"] = lazy(item["link"])(request)
|
294
291
|
|
292
|
+
item["active"] = self._get_is_active(
|
293
|
+
request, item.get("link_callback") or item["link"]
|
294
|
+
)
|
295
295
|
allowed_items.append(item)
|
296
296
|
|
297
297
|
tab["items"] = allowed_items
|
@@ -354,6 +354,19 @@ class UnfoldAdminSite(AdminSite):
|
|
354
354
|
|
355
355
|
return target
|
356
356
|
|
357
|
+
def _process_favicons(
|
358
|
+
self, request: HttpRequest, favicons: List[Dict]
|
359
|
+
) -> List[Favicon]:
|
360
|
+
return [
|
361
|
+
Favicon(
|
362
|
+
href=self._get_value(item["href"], request),
|
363
|
+
rel=item.get("rel"),
|
364
|
+
sizes=item.get("sizes"),
|
365
|
+
type=item.get("type"),
|
366
|
+
)
|
367
|
+
for item in favicons
|
368
|
+
]
|
369
|
+
|
357
370
|
def _process_colors(
|
358
371
|
self, colors: Dict[str, Dict[str, str]]
|
359
372
|
) -> Dict[str, Dict[str, str]]:
|
@@ -365,3 +378,14 @@ class UnfoldAdminSite(AdminSite):
|
|
365
378
|
colors[name][weight] = " ".join(str(item) for item in hex_to_rgb(value))
|
366
379
|
|
367
380
|
return colors
|
381
|
+
|
382
|
+
def _get_is_active(self, request: HttpRequest, link: str) -> bool:
|
383
|
+
if not isinstance(link, str):
|
384
|
+
link = str(link)
|
385
|
+
|
386
|
+
if link in request.path and link != reverse_lazy(f"{self.name}:index"):
|
387
|
+
return True
|
388
|
+
elif link == request.path == reverse_lazy(f"{self.name}:index"):
|
389
|
+
return True
|
390
|
+
|
391
|
+
return False
|