django-unfold 0.51.0__py3-none-any.whl → 0.53.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_unfold-0.51.0.dist-info → django_unfold-0.53.0.dist-info}/METADATA +1 -1
- {django_unfold-0.51.0.dist-info → django_unfold-0.53.0.dist-info}/RECORD +40 -37
- unfold/contrib/filters/admin/dropdown_filters.py +3 -3
- unfold/contrib/filters/admin/mixins.py +1 -1
- unfold/contrib/filters/admin/text_filters.py +2 -2
- unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +1 -1
- unfold/contrib/simple_history/templates/simple_history/object_history.html +2 -2
- unfold/decorators.py +13 -4
- unfold/forms.py +2 -1
- unfold/paginator.py +10 -0
- unfold/sites.py +8 -6
- unfold/static/unfold/css/styles.css +1 -1
- unfold/styles.css +15 -5
- unfold/templates/admin/auth/user/add_form.html +1 -1
- unfold/templates/admin/change_form.html +10 -9
- unfold/templates/admin/pagination.html +4 -20
- unfold/templates/admin/submit_line.html +1 -1
- unfold/templates/unfold/components/table.html +2 -2
- unfold/templates/unfold/helpers/display_dropdown.html +33 -0
- unfold/templates/unfold/helpers/fieldset_row.html +19 -7
- unfold/templates/unfold/helpers/fieldset_row_checkbox.html +1 -1
- unfold/templates/unfold/helpers/fieldset_row_field.html +1 -1
- unfold/templates/unfold/helpers/messages/debug.html +2 -2
- unfold/templates/unfold/helpers/messages/info.html +2 -2
- unfold/templates/unfold/helpers/messages/success.html +2 -2
- unfold/templates/unfold/helpers/messages/warning.html +2 -2
- unfold/templates/unfold/helpers/pagination_default.html +23 -0
- unfold/templates/unfold/helpers/pagination_infinite.html +11 -0
- unfold/templates/unfold/helpers/site_icon.html +14 -14
- unfold/templates/unfold/helpers/tab_action.html +1 -1
- unfold/templates/unfold/helpers/welcomemsg.html +2 -2
- unfold/templates/unfold/widgets/date.html +1 -1
- unfold/templates/unfold/widgets/split_datetime.html +0 -8
- unfold/templates/unfold/widgets/time.html +1 -1
- unfold/templatetags/unfold.py +58 -24
- unfold/templatetags/unfold_list.py +10 -6
- unfold/utils.py +25 -0
- unfold/widgets.py +8 -10
- unfold/templates/unfold/widgets/textarea_expandable.html +0 -7
- {django_unfold-0.51.0.dist-info → django_unfold-0.53.0.dist-info}/LICENSE.md +0 -0
- {django_unfold-0.51.0.dist-info → django_unfold-0.53.0.dist-info}/WHEEL +0 -0
unfold/styles.css
CHANGED
@@ -4,6 +4,11 @@
|
|
4
4
|
|
5
5
|
@tailwind utilities;
|
6
6
|
|
7
|
+
@layer utilities {
|
8
|
+
.field-sizing-content {
|
9
|
+
field-sizing: content;
|
10
|
+
}
|
11
|
+
}
|
7
12
|
|
8
13
|
/*******************************************************
|
9
14
|
Classes
|
@@ -310,7 +315,7 @@ h3 span:nth-child(3) {
|
|
310
315
|
.select2-container.select2-container--admin-autocomplete
|
311
316
|
.select2-selection--single
|
312
317
|
.select2-selection__rendered {
|
313
|
-
@apply font-medium h-9 px-3 py-2 text-
|
318
|
+
@apply font-medium h-9 px-3 py-2 text-font-default-light text-sm dark:text-font-default-dark;
|
314
319
|
}
|
315
320
|
|
316
321
|
.select2-container.select2-container--admin-autocomplete
|
@@ -361,7 +366,7 @@ h3 span:nth-child(3) {
|
|
361
366
|
.select2-container.select2-container--admin-autocomplete
|
362
367
|
.select2-search--dropdown
|
363
368
|
.select2-search__field {
|
364
|
-
@apply bg-base-50 border border-base-200 border-solid flex-grow font-medium mx-0 outline-none px-3 py-2 rounded shadow-sm text-
|
369
|
+
@apply bg-base-50 border border-base-200 border-solid flex-grow font-medium mx-0 outline-none px-3 py-2 rounded shadow-sm text-font-default-light text-sm w-full dark:bg-base-800 dark:border-base-800 dark:text-font-default-dark;
|
365
370
|
}
|
366
371
|
|
367
372
|
.select2-container.select2-container--admin-autocomplete.select2-container--open.select2-container--above {
|
@@ -386,12 +391,17 @@ h3 span:nth-child(3) {
|
|
386
391
|
|
387
392
|
.select2-container.select2-container--admin-autocomplete
|
388
393
|
.select2-results__option {
|
389
|
-
@apply block px-3 py-2 text-
|
394
|
+
@apply block px-3 py-2 text-font-default-light text-sm transition-all dark:text-font-default-dark;
|
390
395
|
}
|
391
396
|
|
392
397
|
.select2-container.select2-container--admin-autocomplete
|
393
|
-
.select2-results__option
|
394
|
-
@apply text-primary-500;
|
398
|
+
.select2-results__option[aria-selected="true"] {
|
399
|
+
@apply text-primary-600 dark:text-primary-500;
|
400
|
+
}
|
401
|
+
|
402
|
+
.select2-container.select2-container--admin-autocomplete
|
403
|
+
.select2-results__option--highlighted[aria-selected="true"] {
|
404
|
+
@apply text-primary-600 dark:text-primary-500;
|
395
405
|
}
|
396
406
|
|
397
407
|
.select2-container.select2-container--admin-autocomplete
|
@@ -6,7 +6,7 @@
|
|
6
6
|
{% if not is_popup %}
|
7
7
|
{% translate 'First, enter a username and password. Then, you’ll be able to edit more user options.' %}
|
8
8
|
{% else %}
|
9
|
-
{% translate "Enter a username and password." %}
|
9
|
+
{% translate "Enter a username and password." %}
|
10
10
|
{% endif %}
|
11
11
|
<p>
|
12
12
|
{% endblock %}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
{% extends "admin/base_site.html" %}
|
2
2
|
|
3
|
-
{% load i18n admin_urls static admin_modify %}
|
3
|
+
{% load i18n admin_urls static admin_modify unfold %}
|
4
4
|
|
5
5
|
{% block extrahead %}{{ block.super }}
|
6
6
|
<script src="{% url 'admin:jsi18n' %}"></script>
|
@@ -58,14 +58,14 @@
|
|
58
58
|
{% endblock %}
|
59
59
|
|
60
60
|
{% block content %}
|
61
|
-
<div id="content-main">
|
61
|
+
<div id="content-main" x-data="{ changeFormWidth: 0 }" x-resize="changeFormWidth = $width">
|
62
62
|
{% block form_before %}{% endblock %}
|
63
63
|
|
64
64
|
{% if adminform.model_admin.change_form_outer_before_template %}
|
65
65
|
{% include adminform.model_admin.change_form_outer_before_template %}
|
66
66
|
{% endif %}
|
67
67
|
|
68
|
-
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}{% if form_url %}action="{{ form_url }}" {% endif %}method="post" id="{{ opts.model_name }}_form" {% if adminform.model_admin.warn_unsaved_form %}class="warn-unsaved-form"{% endif %} novalidate>
|
68
|
+
<form {% if adminform.model_admin.conditional_fields %}x-data='{{ adminform|changeform_data }}'{% endif %} {% if has_file_field %}enctype="multipart/form-data" {% endif %}{% if form_url %}action="{{ form_url }}" {% endif %}method="post" id="{{ opts.model_name }}_form" {% if adminform.model_admin.warn_unsaved_form %}class="warn-unsaved-form"{% endif %} novalidate>
|
69
69
|
{% csrf_token %}
|
70
70
|
|
71
71
|
{% if adminform.model_admin.change_form_before_template %}
|
@@ -87,12 +87,13 @@
|
|
87
87
|
{% include "unfold/helpers/messages/error.html" with errors=adminform.form.non_field_errors %}
|
88
88
|
|
89
89
|
{% block field_sets %}
|
90
|
-
{%
|
91
|
-
{%
|
92
|
-
{%
|
93
|
-
|
94
|
-
|
95
|
-
|
90
|
+
{% with has_conditional_display=adminform.model_admin.conditional_fields %}
|
91
|
+
{% for fieldset in adminform %}
|
92
|
+
{% if "tab" not in fieldset.classes %}
|
93
|
+
{% include 'admin/includes/fieldset.html' %}
|
94
|
+
{% endif %}
|
95
|
+
{% endfor %}
|
96
|
+
{% endwith %}
|
96
97
|
{% include "unfold/helpers/fieldsets_tabs.html" %}
|
97
98
|
{% endblock %}
|
98
99
|
|
@@ -4,28 +4,12 @@
|
|
4
4
|
<div class="{% if not is_popup %}max-w-full 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 %} x-bind:style="'width: ' + mainWidth + 'px'">
|
5
5
|
<div class="lg:backdrop-blur-sm lg:bg-white/80 lg:flex lg:items-center lg:dark:bg-base-900/80 {% if not is_popup %}lg:border-t lg:border-base-200 lg:h-[71px] lg:py-2 lg:relative lg:scrollable-top lg:px-8 lg:dark:border-base-800{% endif %}">
|
6
6
|
<div class="flex flex-row items-center {% if not cl.model_admin.list_fullwidth %}lg:mx-auto{% endif %}" x-bind:style="'width: ' + changeListWidth + 'px'">
|
7
|
-
{% if
|
8
|
-
{%
|
9
|
-
|
10
|
-
|
11
|
-
</div>
|
12
|
-
{% endfor %}
|
7
|
+
{% if cl.paginator.template_name %}
|
8
|
+
{% include cl.paginator.template_name %}
|
9
|
+
{% else %}
|
10
|
+
{% include "unfold/helpers/pagination_default.html" %}
|
13
11
|
{% endif %}
|
14
12
|
|
15
|
-
<div class="py-4">
|
16
|
-
{% if pagination_required %}
|
17
|
-
-
|
18
|
-
{% endif %}
|
19
|
-
|
20
|
-
{{ cl.result_count }}
|
21
|
-
|
22
|
-
{% if cl.result_count == 1 %}
|
23
|
-
{{ cl.opts.verbose_name }}
|
24
|
-
{% else %}
|
25
|
-
{{ cl.opts.verbose_name_plural }}
|
26
|
-
{% endif %}
|
27
|
-
</div>
|
28
|
-
|
29
13
|
{% if show_all_url %}
|
30
14
|
<a href="{{ show_all_url }}" class="showall ml-4 text-primary-600 dark:text-primary-500">
|
31
15
|
{% translate 'Show all' %}
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<div {% if not is_popup %}id="submit-row"{% endif %} class="relative {% if not is_popup %}lg:mt-16{% endif %} z-20">
|
4
4
|
<div class="{% if not is_popup %}max-w-full 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}" x-bind:style="'width: ' + mainWidth + 'px'"{% endif %}>
|
5
5
|
<div class="backdrop-blur-sm bg-white/80 pb-4 dark:bg-base-900/80 {% if not is_popup %}lg:border-t lg:border-base-200 lg:py-4 relative lg:scrollable-top lg:px-8 dark:border-base-800{% endif %}">
|
6
|
-
<div class="flex flex-col-reverse gap-3 items-center mx-auto lg:flex-row-reverse">
|
6
|
+
<div class="flex flex-col-reverse gap-3 items-center mx-auto lg:flex-row-reverse" x-bind:style="'width: ' + changeFormWidth + 'px'">
|
7
7
|
{% block submit-row %}
|
8
8
|
{% if show_save %}
|
9
9
|
<button type="submit" name="_save" class="bg-primary-600 block border border-transparent font-medium px-3 py-2 rounded text-white w-full lg:w-auto">
|
@@ -12,9 +12,9 @@
|
|
12
12
|
<table class="block border-spacing-none border-separate w-full lg:table">
|
13
13
|
{% if table.headers %}
|
14
14
|
<thead class="text-base-900 dark:text-base-100 {% if height %}sticky top-0{% endif %}">
|
15
|
-
<tr class="bg-base-50 dark:bg-
|
15
|
+
<tr class="bg-base-50 dark:bg-base-900">
|
16
16
|
{% for header in table.headers %}
|
17
|
-
<th class="align-middle border-b border-base-200 font-semibold py-2 text-left text-sm whitespace-nowrap sortable column-description hidden px-3 lg:table-cell dark:border-base-800 {% if card_included == 1 %}first:pl-6 last:pr-6{% endif %}">
|
17
|
+
<th class="align-middle border-b border-base-200 font-semibold py-2 text-left text-sm whitespace-nowrap sortable column-description hidden px-3 lg:table-cell dark:border-base-800 dark:bg-white/[.02] {% if card_included == 1 %}first:pl-6 last:pr-6{% endif %}">
|
18
18
|
{{ header|capfirst }}
|
19
19
|
</th>
|
20
20
|
{% endfor %}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
{% with dropdown_id=instance.pk|cut:"-"|add:field_name %}
|
2
|
+
{% with total=value.items|length %}
|
3
|
+
<div x-data="{ openDropdownId{{ dropdown_id }}: false }">
|
4
|
+
<div class="flex flex-row gap-1.5 items-center {% if total > 0 or value.content %}cursor-pointer{% endif %}" {% if total > 0 or value.content %}x-ref="rowDropdown{{ dropdown_id }}" x-on:click="openDropdownId{{ dropdown_id }} = !openDropdownId{{ dropdown_id }}"{% endif %}>
|
5
|
+
{% if total > 0 or value.content %}
|
6
|
+
<span class="material-symbols-outlined">unfold_more</span>
|
7
|
+
{% endif %}
|
8
|
+
|
9
|
+
<span>
|
10
|
+
{{ value.title }}
|
11
|
+
</span>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
{% if total > 0 or value.content %}
|
15
|
+
<template x-teleport="body">
|
16
|
+
{% if value.content %}
|
17
|
+
<div class="bg-white border overflow-y-auto overflow-x-hidden p-3 rounded shadow-lg text-sm top-7 z-50 w-48 dark:bg-base-800 dark:border-base-700" data-simplebar x-cloak x-transition x-anchor.bottom-start.offset.4="$refs.rowDropdown{{ dropdown_id }}" x-show="openDropdownId{{ dropdown_id }}"x-on:click.outside="openDropdownId{{ dropdown_id }} = false" {% if value.width or value.height %}style="{% if value.width %}width: {{ value.width }}px;{% endif %}{% if value.height %}height: {{ value.height }}px;{% endif %}"{% endif %}>
|
18
|
+
{{ value.content }}
|
19
|
+
</div>
|
20
|
+
{% else %}
|
21
|
+
<nav class="bg-white border overflow-y-auto overflow-x-hidden flex flex-col py-1 rounded shadow-lg text-sm top-7 z-50 w-48 dark:bg-base-800 dark:border-base-700" data-simplebar x-cloak x-transition x-anchor.bottom-start.offset.4="$refs.rowDropdown{{ dropdown_id }}" x-show="openDropdownId{{ dropdown_id }}"x-on:click.outside="openDropdownId{{ dropdown_id }} = false" {% if value.width or value.height %}style="{% if value.width %}width: {{ value.width }}px;{% endif %}{% if value.height %}height: {{ value.height }}px;{% endif %}"{% endif %}>
|
22
|
+
{% for item in value.items %}
|
23
|
+
<{% if item.link %}a{% else %}span{% endif %} {% if item.link %}href="{{ item.link }}"{% endif %} class="flex items-center gap-2 mx-1 px-3 py-2 max-h-[30px] rounded hover:bg-base-100 dark:hover:bg-base-700 dark:hover:text-base-200 {% if value.striped %}{% cycle '' 'bg-base-50 dark:bg-white/[.04]' %}{% endif %}">
|
24
|
+
<span class="grow truncate">{{ item.title }}</span>
|
25
|
+
</{% if item.link %}a{% else %}span{% endif %}>
|
26
|
+
{% endfor %}
|
27
|
+
</nav>
|
28
|
+
{% endif %}
|
29
|
+
</template>
|
30
|
+
{% endif %}
|
31
|
+
</div>
|
32
|
+
{% endwith %}
|
33
|
+
{% endwith %}
|
@@ -2,12 +2,24 @@
|
|
2
2
|
|
3
3
|
<div class="{% fieldset_row_classes %} {% if forloop.last %}last{% endif %}">
|
4
4
|
{% for field in line %}
|
5
|
-
|
6
|
-
{% if
|
7
|
-
{%
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
{% with adminform.model_admin.conditional_fields|index:field.field.name as conditional_display %}
|
6
|
+
<div class="{% fieldset_line_classes %}" {% if has_conditional_display and conditional_display %}x-show="{{ conditional_display }}"{% endif %}>
|
7
|
+
{% if has_conditional_display %}
|
8
|
+
{% with field|changeform_condition as field %}
|
9
|
+
{% if field.is_checkbox %}
|
10
|
+
{% include "unfold/helpers/fieldset_row_checkbox.html" %}
|
11
|
+
{% else %}
|
12
|
+
{% include "unfold/helpers/fieldset_row_field.html" %}
|
13
|
+
{% endif %}
|
14
|
+
{% endwith %}
|
15
|
+
{% else %}
|
16
|
+
{% if field.is_checkbox %}
|
17
|
+
{% include "unfold/helpers/fieldset_row_checkbox.html" %}
|
18
|
+
{% else %}
|
19
|
+
{% include "unfold/helpers/fieldset_row_field.html" %}
|
20
|
+
{% endif %}
|
21
|
+
{% endif %}
|
22
|
+
</div>
|
23
|
+
{% endwith %}
|
12
24
|
{% endfor %}
|
13
25
|
</div>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
{% if adminform.model_admin.compressed_fields %}
|
2
2
|
<div class="flex flex-col gap-4 py-2 lg:flex-row">
|
3
|
-
<div class="font-medium flex items-start -ml-2 min-w-32 w-32 lg:min-w-
|
3
|
+
<div class="font-medium flex items-start -ml-2 min-w-32 w-32 lg:min-w-56 lg:w-56">
|
4
4
|
{{ field.label_tag }}
|
5
5
|
</div>
|
6
6
|
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<
|
1
|
+
<div class="mb-3 px-3 py-3 rounded text-sm last:mb-8 bg-base-100 text-base-700 dark:bg-base-500/20 dark:text-base-400">
|
2
2
|
{{ message }}
|
3
|
-
</
|
3
|
+
</div>
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<
|
1
|
+
<div class="mb-3 px-3 py-3 rounded text-sm last:mb-8 bg-blue-100 text-blue-700 dark:bg-blue-500/20 dark:text-blue-400">
|
2
2
|
{{ message }}
|
3
|
-
</
|
3
|
+
</div>
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<
|
1
|
+
<div class="mb-3 px-3 py-3 rounded text-sm last:mb-8 bg-green-100 text-green-700 dark:bg-green-500/20 dark:text-green-400">
|
2
2
|
{{ message }}
|
3
|
-
</
|
3
|
+
</div>
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<
|
1
|
+
<div class="mb-3 px-3 py-3 rounded text-sm last:mb-8 bg-orange-100 text-orange-700 dark:bg-orange-500/20 dark:text-orange-400">
|
2
2
|
{{ message }}
|
3
|
-
</
|
3
|
+
</div>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
{% load unfold_list %}
|
2
|
+
|
3
|
+
{% if pagination_required %}
|
4
|
+
{% for i in page_range %}
|
5
|
+
<div class="{% if forloop.last %}pr-2{% else %}pr-4{% endif %}">
|
6
|
+
{% paginator_number cl i %}
|
7
|
+
</div>
|
8
|
+
{% endfor %}
|
9
|
+
{% endif %}
|
10
|
+
|
11
|
+
<div class="py-4">
|
12
|
+
{% if pagination_required %}
|
13
|
+
-
|
14
|
+
{% endif %}
|
15
|
+
|
16
|
+
{{ cl.result_count }}
|
17
|
+
|
18
|
+
{% if cl.result_count == 1 %}
|
19
|
+
{{ cl.opts.verbose_name }}
|
20
|
+
{% else %}
|
21
|
+
{{ cl.opts.verbose_name_plural }}
|
22
|
+
{% endif %}
|
23
|
+
</div>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
{% load unfold_list i18n %}
|
2
|
+
|
3
|
+
<div class="flex flex-row gap-4">
|
4
|
+
<a {% if cl.page_num != 1 %}href="?p={{ cl.page_num|add:-1 }}"{% endif %} class="{% if cl.page_num != 1 %}hover:text-primary-600 dark:hover:text-primary-500{% endif %}">
|
5
|
+
{% trans "Previous" %}
|
6
|
+
</a>
|
7
|
+
|
8
|
+
<a href="?p={{ cl.page_num|add:1 }}" class="hover:text-primary-600 dark:hover:text-primary-500">
|
9
|
+
{% trans "Next" %}
|
10
|
+
</a>
|
11
|
+
</div>
|
@@ -2,29 +2,29 @@
|
|
2
2
|
|
3
3
|
{% if site_icon %}
|
4
4
|
<div class="shrink-0">
|
5
|
-
<a href="{% url "admin:index" %}">
|
6
|
-
|
7
|
-
|
5
|
+
<{% if site_dropdown %}span{% else %}a href="{% url "admin:index" %}"{% endif %}>
|
6
|
+
{% if site_icon.light and site_icon.dark %}
|
7
|
+
<img src="{{ site_icon.dark }}" alt="{% trans 'Home' %}" class="h-8 hidden dark:block"/>
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
<img src="{{ site_icon.light }}" alt="{% trans 'Home' %}" class="block h-8 dark:hidden" />
|
10
|
+
{% else %}
|
11
|
+
<img src="{{ site_icon }}" class="h-8" alt="{% trans 'Home' %}" />
|
12
|
+
{% endif %}
|
13
|
+
</{% if site_dropdown %}span{% else %}a{% endif %}>
|
14
14
|
</div>
|
15
15
|
{% else %}
|
16
|
-
<a href="{% url "admin:index" %}" class="bg-primary-600 flex h-8 items-center justify-center rounded text-white text-xs w-8">
|
17
|
-
|
18
|
-
</a>
|
16
|
+
<{% if site_dropdown %}span{% else %}a href="{% url "admin:index" %}"{% endif %} class="bg-primary-600 flex h-8 items-center justify-center rounded shrink-0 text-white text-xs w-8">
|
17
|
+
<span class="material-symbols-outlined md-18">{% if site_symbol %}{{ site_symbol }}{% else %}settings{% endif %}</span>
|
18
|
+
</{% if site_dropdown %}span{% else %}a{% endif %}>
|
19
19
|
{% endif %}
|
20
20
|
|
21
|
-
<div class="flex flex-col grow min-w-0">
|
22
|
-
<div class="text-font-important-light leading-normal tracking-tight dark:text-font-important-dark *:leading-none {% if site_subheader %}xl:text-sm{% else %}xl:text-base{% endif %}">
|
21
|
+
<div class="flex flex-col gap-0.5 grow justify-center min-w-0">
|
22
|
+
<div class="text-font-important-light leading-normal tracking-tight dark:text-font-important-dark *:leading-none *:truncate {% if site_subheader %}xl:text-sm{% else %}xl:text-base{% endif %}">
|
23
23
|
{{ branding }}
|
24
24
|
</div>
|
25
25
|
|
26
26
|
{% if site_subheader %}
|
27
|
-
<div class="font-normal
|
27
|
+
<div class="font-normal text-font-subtle-light text-xs leading-none dark:text-font-subtle-dark">
|
28
28
|
{{ site_subheader }}
|
29
29
|
</div>
|
30
30
|
{% endif %}
|
@@ -25,7 +25,7 @@
|
|
25
25
|
<template x-teleport="body">
|
26
26
|
<nav x-anchor.bottom-end.offset.4="$refs.actionDropdown{{ action.method_name }}" class="absolute bg-white border flex flex-col -mr-px py-1 right-0 rounded shadow-lg top-10 w-52 z-50 dark:bg-base-800 dark:border-base-700" x-transition x-show="actionDropdownOpen" x-on:click.outside="actionDropdownOpen = false">
|
27
27
|
{% for item in action.items %}
|
28
|
-
<a href="{{ item.path }}" class="flex items-center font-normal gap-2 max-h-[30px] mx-1 px-3 py-2 rounded text-left hover:bg-base-100 hover:text-base-700 dark:hover:bg-base-700 dark:hover:text-base-200"{% if blank %} target="_blank"{% endif %} {% include "unfold/helpers/attrs.html" with attrs=
|
28
|
+
<a href="{{ item.path }}" class="flex items-center font-normal gap-2 max-h-[30px] mx-1 px-3 py-2 rounded text-left hover:bg-base-100 hover:text-base-700 dark:hover:bg-base-700 dark:hover:text-base-200"{% if blank %} target="_blank"{% endif %} {% include "unfold/helpers/attrs.html" with attrs=item.attrs %}>
|
29
29
|
{% if item.icon %}
|
30
30
|
<span class="material-symbols-outlined">
|
31
31
|
{{ item.icon }}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
{% load i18n %}
|
1
|
+
{% load unfold i18n %}
|
2
2
|
|
3
3
|
<div class="flex flex-row flex-grow font-semibold items-center min-w-0 mr-3">
|
4
4
|
<span class="cursor-pointer flex flex-row items-center">
|
@@ -23,7 +23,7 @@
|
|
23
23
|
{{ content_title }}
|
24
24
|
</span>
|
25
25
|
|
26
|
-
{% if cl and cl.full_result_count != cl.result_count %}
|
26
|
+
{% if cl and cl.full_result_count != cl.result_count and cl.paginator|class_name != "InfinitePaginator" %}
|
27
27
|
<span class="font-medium ml-2 text-font-subtle-light text-sm dark:text-font-subtle-dark">
|
28
28
|
{% blocktranslate count counter=cl.result_count %}{{ counter }} result{% plural %}{{ counter }} results{% endblocktranslate %} (<a href="?{% if cl.is_popup %}_popup=1{% endif %}">{% if cl.show_full_result_count %}{% blocktranslate with full_result_count=cl.full_result_count %}{{ full_result_count }} total{% endblocktranslate %}{% else %}{% translate "Show all" %}{% endif %}</a>)
|
29
29
|
</span>
|
@@ -1,9 +1,5 @@
|
|
1
1
|
<div class="datetime flex flex-col gap-2 max-w-2xl lg:flex-row lg:group-[.field-row]:flex-row lg:group-[.field-row]:items-center lg:group-[.field-tabular]:flex-row lg:group-[.field-tabular]:items-center">
|
2
2
|
<div class="basis-1/2 flex flex-col lg:group-[.field-row]:flex-row group-[.field-row]:gap-2 lg:group-[.field-tabular]:flex-row group-[.field-tabular]:gap-2">
|
3
|
-
<div class="font-medium mb-2 group-[.field-row]:mb-0 group-[.field-row]:flex group-[.field-row]:items-center group-[.field-tabular]:mb-0 group-[.field-tabular]:flex group-[.field-tabular]:items-center">
|
4
|
-
{{ date_label }}
|
5
|
-
</div>
|
6
|
-
|
7
3
|
{% with widget=widget.subwidgets.0 %}
|
8
4
|
<div class="flex flex-col min-w-52 relative group-[.field-row]:flex-grow group-[.field-tabular]:flex-grow">
|
9
5
|
{% include widget.template_name %}
|
@@ -12,10 +8,6 @@
|
|
12
8
|
</div>
|
13
9
|
|
14
10
|
<div class="basis-1/2 flex flex-col lg:ml-auto md:mt-0 lg:group-[.field-row]:flex-row group-[.field-row]:gap-2 lg:group-[.field-tabular]:flex-row group-[.field-tabular]:gap-2">
|
15
|
-
<div class="font-medium mb-2 group-[.field-row]:mb-0 group-[.field-row]:flex group-[.field-row]:items-center group-[.field-tabular]:mb-0 group-[.field-tabular]:flex group-[.field-tabular]:items-center">
|
16
|
-
{{ time_label }}
|
17
|
-
</div>
|
18
|
-
|
19
11
|
{% with widget=widget.subwidgets.1 %}
|
20
12
|
<div class="flex flex-col min-w-52 relative group-[.field-row]:flex-grow group-[.field-tabular]:flex-grow">
|
21
13
|
{% include widget.template_name %}
|
unfold/templatetags/unfold.py
CHANGED
@@ -1,21 +1,24 @@
|
|
1
|
+
import json
|
1
2
|
from collections.abc import Mapping
|
2
3
|
from typing import Any, Optional, Union
|
3
4
|
|
4
5
|
from django import template
|
5
6
|
from django.contrib.admin.helpers import AdminForm, Fieldset
|
6
7
|
from django.contrib.admin.views.main import ChangeList
|
8
|
+
from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
|
7
9
|
from django.db.models import Model
|
8
10
|
from django.db.models.options import Options
|
9
|
-
from django.forms import Field
|
11
|
+
from django.forms import BoundField, Field
|
10
12
|
from django.http import HttpRequest
|
11
13
|
from django.template import Context, Library, Node, RequestContext, TemplateSyntaxError
|
12
14
|
from django.template.base import NodeList, Parser, Token, token_kwargs
|
13
15
|
from django.template.loader import render_to_string
|
14
|
-
from django.utils.safestring import SafeText
|
16
|
+
from django.utils.safestring import SafeText, mark_safe
|
15
17
|
|
16
18
|
from unfold.components import ComponentRegistry
|
17
19
|
from unfold.dataclasses import UnfoldAction
|
18
20
|
from unfold.enums import ActionVariant
|
21
|
+
from unfold.widgets import UnfoldAdminSplitDateTimeWidget
|
19
22
|
|
20
23
|
register = Library()
|
21
24
|
|
@@ -107,7 +110,10 @@ def class_name(value: Any) -> str:
|
|
107
110
|
|
108
111
|
@register.filter
|
109
112
|
def index(indexable: Mapping[int, Any], i: int) -> Any:
|
110
|
-
|
113
|
+
try:
|
114
|
+
return indexable[i]
|
115
|
+
except (KeyError, TypeError):
|
116
|
+
return None
|
111
117
|
|
112
118
|
|
113
119
|
@register.filter
|
@@ -346,7 +352,6 @@ def fieldset_row_classes(context: Context) -> str:
|
|
346
352
|
]
|
347
353
|
|
348
354
|
formset = context.get("inline_admin_formset", None)
|
349
|
-
adminform = context.get("adminform", None)
|
350
355
|
line = context.get("line")
|
351
356
|
|
352
357
|
# Hide the field in case of ordering field for sorting
|
@@ -359,27 +364,11 @@ def fieldset_row_classes(context: Context) -> str:
|
|
359
364
|
):
|
360
365
|
classes.append("hidden")
|
361
366
|
|
362
|
-
# Compressed fields special styling
|
363
|
-
if (
|
364
|
-
adminform
|
365
|
-
and hasattr(adminform.model_admin, "compressed_fields")
|
366
|
-
and adminform.model_admin.compressed_fields
|
367
|
-
):
|
368
|
-
classes.extend(
|
369
|
-
[
|
370
|
-
"lg:border-b",
|
371
|
-
"lg:border-base-200",
|
372
|
-
"dark:lg:border-base-800",
|
373
|
-
"last:lg:border-b-0",
|
374
|
-
]
|
375
|
-
)
|
376
|
-
|
377
367
|
if len(line.fields) > 1:
|
378
368
|
classes.extend(
|
379
369
|
[
|
380
|
-
"
|
381
|
-
"
|
382
|
-
"flex-wrap",
|
370
|
+
"grid",
|
371
|
+
f"lg:grid-cols-{len(line.fields)}",
|
383
372
|
]
|
384
373
|
)
|
385
374
|
|
@@ -397,7 +386,8 @@ def fieldset_line_classes(context: Context) -> str:
|
|
397
386
|
"flex-col",
|
398
387
|
"flex-grow",
|
399
388
|
"group/line",
|
400
|
-
"
|
389
|
+
"px-3",
|
390
|
+
"py-2.5",
|
401
391
|
]
|
402
392
|
field = context.get("field")
|
403
393
|
adminform = context.get("adminform")
|
@@ -417,8 +407,8 @@ def fieldset_line_classes(context: Context) -> str:
|
|
417
407
|
[
|
418
408
|
"border-b",
|
419
409
|
"border-base-200",
|
410
|
+
"border-dashed",
|
420
411
|
"group-[.last]/row:border-b-0",
|
421
|
-
"lg:border-b-0",
|
422
412
|
"lg:border-l",
|
423
413
|
"lg:flex-row",
|
424
414
|
"dark:border-base-800",
|
@@ -495,3 +485,47 @@ def action_item_classes(context: Context, action: UnfoldAction) -> str:
|
|
495
485
|
)
|
496
486
|
|
497
487
|
return " ".join(set(classes))
|
488
|
+
|
489
|
+
|
490
|
+
@register.filter
|
491
|
+
def changeform_data(adminform: AdminForm) -> str:
|
492
|
+
fields = []
|
493
|
+
|
494
|
+
for fieldset in adminform:
|
495
|
+
for line in fieldset:
|
496
|
+
for field in line:
|
497
|
+
if isinstance(field.field, dict):
|
498
|
+
continue
|
499
|
+
|
500
|
+
if isinstance(field.field.field.widget, UnfoldAdminSplitDateTimeWidget):
|
501
|
+
for index, _widget in enumerate(field.field.field.widget.widgets):
|
502
|
+
fields.append(
|
503
|
+
f"{field.field.name}{field.field.field.widget.widgets_names[index]}"
|
504
|
+
)
|
505
|
+
else:
|
506
|
+
fields.append(field.field.name)
|
507
|
+
|
508
|
+
return mark_safe(json.dumps(dict.fromkeys(fields, "")))
|
509
|
+
|
510
|
+
|
511
|
+
@register.filter(takes_context=True)
|
512
|
+
def changeform_condition(field: BoundField) -> BoundField:
|
513
|
+
if isinstance(field.field, dict):
|
514
|
+
return field
|
515
|
+
|
516
|
+
if isinstance(field.field.field.widget, RelatedFieldWidgetWrapper):
|
517
|
+
field.field.field.widget.widget.attrs["x-model.fill"] = field.field.name
|
518
|
+
field.field.field.widget.widget.attrs["x-init"] = mark_safe(
|
519
|
+
f"const $ = django.jQuery; $(function () {{ const select = $('#{field.field.auto_id}'); select.on('change', (ev) => {{ {field.field.name} = select.val(); }}); }});"
|
520
|
+
)
|
521
|
+
elif isinstance(field.field.field.widget, UnfoldAdminSplitDateTimeWidget):
|
522
|
+
for index, widget in enumerate(field.field.field.widget.widgets):
|
523
|
+
field_name = (
|
524
|
+
f"{field.field.name}{field.field.field.widget.widgets_names[index]}"
|
525
|
+
)
|
526
|
+
|
527
|
+
widget.attrs["x-model.fill"] = field_name
|
528
|
+
else:
|
529
|
+
field.field.field.widget.attrs["x-model.fill"] = field.field.name
|
530
|
+
|
531
|
+
return field
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import datetime
|
2
|
+
from collections.abc import Generator
|
2
3
|
from typing import Any, Optional, Union
|
3
4
|
|
4
5
|
from django.contrib.admin.templatetags.admin_list import (
|
@@ -18,7 +19,6 @@ from django.core.exceptions import ObjectDoesNotExist
|
|
18
19
|
from django.db import models
|
19
20
|
from django.db.models import Model
|
20
21
|
from django.forms import Form
|
21
|
-
from django.http import HttpRequest
|
22
22
|
from django.template import Library
|
23
23
|
from django.template.base import Parser, Token
|
24
24
|
from django.template.loader import render_to_string
|
@@ -28,6 +28,7 @@ from django.utils.safestring import SafeText, mark_safe
|
|
28
28
|
from django.utils.translation import gettext_lazy as _
|
29
29
|
|
30
30
|
from unfold.utils import (
|
31
|
+
display_for_dropdown,
|
31
32
|
display_for_field,
|
32
33
|
display_for_header,
|
33
34
|
display_for_label,
|
@@ -189,11 +190,9 @@ def result_headers(cl):
|
|
189
190
|
}
|
190
191
|
|
191
192
|
|
192
|
-
def items_for_result(
|
193
|
-
|
194
|
-
|
195
|
-
"""
|
196
|
-
|
193
|
+
def items_for_result(
|
194
|
+
cl: ChangeList, result: Model, form
|
195
|
+
) -> Generator[SafeText, None, None]:
|
197
196
|
def link_in_col(is_first: bool, field_name: str, cl: ChangeList) -> bool:
|
198
197
|
if cl.list_display_links is None:
|
199
198
|
return False
|
@@ -226,9 +225,14 @@ def items_for_result(cl: ChangeList, result: HttpRequest, form) -> SafeText:
|
|
226
225
|
boolean = getattr(attr, "boolean", False)
|
227
226
|
label = getattr(attr, "label", False)
|
228
227
|
header = getattr(attr, "header", False)
|
228
|
+
dropdown = getattr(attr, "dropdown", False)
|
229
229
|
|
230
230
|
if label:
|
231
231
|
result_repr = display_for_label(value, empty_value_display, label)
|
232
|
+
elif dropdown:
|
233
|
+
result_repr = display_for_dropdown(
|
234
|
+
result, field_name, value, empty_value_display
|
235
|
+
)
|
232
236
|
elif header:
|
233
237
|
result_repr = display_for_header(value, empty_value_display)
|
234
238
|
else:
|