django-unfold 0.68.0__py3-none-any.whl → 0.69.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.68.0.dist-info → django_unfold-0.69.0.dist-info}/METADATA +31 -42
- {django_unfold-0.68.0.dist-info → django_unfold-0.69.0.dist-info}/RECORD +33 -29
- unfold/admin.py +8 -37
- unfold/datasets.py +22 -1
- unfold/forms.py +22 -15
- unfold/mixins/__init__.py +2 -1
- unfold/mixins/dataset_model_admin.py +62 -0
- unfold/settings.py +1 -0
- unfold/sites.py +5 -1
- unfold/static/admin/js/actions.js +246 -0
- unfold/static/unfold/css/styles.css +2 -2
- unfold/static/unfold/fonts/material-symbols/Material-Symbols-Outlined.woff2 +0 -0
- unfold/templates/admin/actions.html +2 -2
- unfold/templates/admin/change_form.html +8 -4
- unfold/templates/admin/change_list.html +1 -1
- unfold/templates/admin/change_list_results.html +1 -1
- unfold/templates/admin/dataset_actions.html +50 -0
- unfold/templates/admin/edit_inline/stacked.html +1 -7
- unfold/templates/admin/edit_inline/tabular.html +1 -7
- unfold/templates/admin/includes/fieldset.html +1 -3
- unfold/templates/admin/search_form.html +1 -1
- unfold/templates/registration/password_change_done.html +3 -4
- unfold/templates/registration/password_change_form.html +10 -6
- unfold/templates/unfold/helpers/change_list_actions.html +1 -1
- unfold/templates/unfold/helpers/dataset.html +19 -7
- unfold/templates/unfold/helpers/fieldsets_tabs.html +9 -11
- unfold/templates/unfold/helpers/inline_heading.html +11 -0
- unfold/templates/unfold/helpers/tab_items.html +6 -4
- unfold/templatetags/unfold.py +47 -70
- unfold/templatetags/unfold_list.py +12 -0
- unfold/widgets.py +1 -2
- {django_unfold-0.68.0.dist-info → django_unfold-0.69.0.dist-info}/WHEEL +0 -0
- {django_unfold-0.68.0.dist-info → django_unfold-0.69.0.dist-info}/licenses/LICENSE.md +0 -0
|
Binary file
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{% load i18n %}
|
|
2
2
|
|
|
3
|
-
<div id="changelist-actions" class="actions flex flex-col gap-3 text-white sm:flex-row sm:items-center lg:items-center {% if not cl.model_admin.list_fullwidth %}mx-auto{% endif %}"
|
|
3
|
+
<div id="changelist-actions" class="actions flex flex-col gap-3 text-white sm:flex-row sm:items-center lg:items-center {% if not cl.model_admin.list_fullwidth %}mx-auto{% endif %}" x-bind:style="'width: ' + changeListWidth + 'px'">
|
|
4
4
|
{% block actions %}
|
|
5
|
-
<div class="group primary flex flex-row gap-2 lg:flex-row" x-data="{action: ''}">
|
|
5
|
+
<div class="group primary flex flex-row gap-2 lg:flex-row" x-data="{action: '', selectAcross: 0}">
|
|
6
6
|
{% block actions-form %}
|
|
7
7
|
{% for field in action_form %}
|
|
8
8
|
{% if field.label %}
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
|
|
44
44
|
{% block form_top %}{% endblock %}
|
|
45
45
|
|
|
46
|
-
<div class="flex flex-col gap-
|
|
46
|
+
<div class="flex flex-col gap-6">
|
|
47
47
|
{% if is_popup %}
|
|
48
48
|
<input type="hidden" name="{{ is_popup_var }}" value="1">
|
|
49
49
|
{% endif %}
|
|
@@ -99,9 +99,13 @@
|
|
|
99
99
|
{% endif %}
|
|
100
100
|
</form>
|
|
101
101
|
|
|
102
|
-
{%
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
{% if datasets %}
|
|
103
|
+
<div class="flex flex-col">
|
|
104
|
+
{% for dataset in datasets %}
|
|
105
|
+
{{ dataset.contents }}
|
|
106
|
+
{% endfor %}
|
|
107
|
+
</div>
|
|
108
|
+
{% endif %}
|
|
105
109
|
|
|
106
110
|
{% if adminform.model_admin.change_form_outer_after_template %}
|
|
107
111
|
{% include adminform.model_admin.change_form_outer_after_template %}
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
{% endif %}
|
|
46
46
|
|
|
47
47
|
<div class="flex -mx-4 module{% if cl.has_filters %} filtered{% endif %}" id="changelist" x-data="{ changeListWidth: 0 }">
|
|
48
|
-
<div class="changelist-form-container flex flex-row grow gap-6 min-w-0 px-4">
|
|
48
|
+
<div class="result-list-wrapper changelist-form-container flex flex-row grow gap-6 min-w-0 px-4">
|
|
49
49
|
<div class="grow min-w-0" x-resize="changeListWidth = $width">
|
|
50
50
|
{% block date_hierarchy %}
|
|
51
51
|
{% if cl.date_hierarchy %}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
{% if results %}
|
|
10
10
|
<div class="{% if cl.search_fields or cl.has_filters %}lg:rounded-b-default{% else %}lg:rounded-default{% endif %} -mx-1 px-1 overflow-x-auto lg:border lg:border-base-200 lg:mx-0 lg:px-0 lg:shadow-xs lg:dark:border-base-800 lg:bg-white lg:dark:bg-base-900 {% if cl.model_admin.list_horizontal_scrollbar_top %}simplebar-horizontal-scrollbar-top{% endif %}" data-simplebar data-simplebar-auto-hide="false">
|
|
11
|
-
<table id="result_list" class="block border-base-200 border-spacing-none border-separate w-full lg:table" {% if cl.model_admin.ordering_field %}x-sort.ghost x-on:end="sortRecords"{% endif %} data-ordering-field="{{ cl.model_admin.ordering_field }}">
|
|
11
|
+
<table id="result_list" class="result-list block border-base-200 border-spacing-none border-separate w-full lg:table" {% if cl.model_admin.ordering_field %}x-sort.ghost x-on:end="sortRecords"{% endif %} data-ordering-field="{{ cl.model_admin.ordering_field }}">
|
|
12
12
|
{% include 'unfold/helpers/change_list_headers.html' %}
|
|
13
13
|
|
|
14
14
|
{% for result in results %}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{% load i18n %}
|
|
2
|
+
|
|
3
|
+
<div class="actions flex flex-col gap-3 order-last lg:order-none lg:flex-row lg:items-center">
|
|
4
|
+
{% if actions_selection_counter %}
|
|
5
|
+
<div class="bg-base-100 flex flex-row h-9.5 items-center px-3 rounded-md shadow-sm truncate dark:bg-white/[.04] lg:bg-transparent dark:lg:!bg-transparent lg:px-0 lg:shadow-none">
|
|
6
|
+
<span class="action-counter text-sm" data-actions-icnt="{{ cl.result_list|length }}">
|
|
7
|
+
{{ selection_note }}
|
|
8
|
+
</span>
|
|
9
|
+
|
|
10
|
+
{% if cl.result_count != cl.result_list|length %}
|
|
11
|
+
<span class="all hidden">
|
|
12
|
+
{{ selection_note_all }}
|
|
13
|
+
</span>
|
|
14
|
+
|
|
15
|
+
{% if not cl.model_admin.list_disable_select_all %}
|
|
16
|
+
<span class="question ml-2 hidden text-primary-600 dark:text-primary-500">
|
|
17
|
+
<a href="#" title="{% translate "Click here to select the objects across all pages" %}">
|
|
18
|
+
{% blocktranslate with cl.result_count as total_count %}Select all {{ total_count }} {{ module_name }}{% endblocktranslate %}
|
|
19
|
+
</a>
|
|
20
|
+
</span>
|
|
21
|
+
|
|
22
|
+
<span class="clear hidden text-red-600">
|
|
23
|
+
<a href="#">
|
|
24
|
+
{% translate "Clear selection" %}
|
|
25
|
+
</a>
|
|
26
|
+
</span>
|
|
27
|
+
{% endif %}
|
|
28
|
+
{% endif %}
|
|
29
|
+
</div>
|
|
30
|
+
{% endif %}
|
|
31
|
+
|
|
32
|
+
<div class="flex flex-col gap-2 lg:flex-row">
|
|
33
|
+
{% for field in action_form %}
|
|
34
|
+
{% if field.label %}
|
|
35
|
+
<label>
|
|
36
|
+
{{ field.label }}
|
|
37
|
+
{% endif %}
|
|
38
|
+
|
|
39
|
+
{{ field }}
|
|
40
|
+
|
|
41
|
+
{% if field.label %}
|
|
42
|
+
</label>
|
|
43
|
+
{% endif %}
|
|
44
|
+
{% endfor %}
|
|
45
|
+
|
|
46
|
+
<button type="submit" form="dataset-{{ id }}" x-show="action" class="bg-primary-600 cursor-pointer flex font-medium items-center justify-center px-3 py-2 rounded-md text-sm text-white whitespace-nowrap" title="{% translate "Run the selected action" %}" name="index" value="{{ action_index|default:0 }}">
|
|
47
|
+
{% trans "Run" %}
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
@@ -4,13 +4,7 @@
|
|
|
4
4
|
<fieldset class="module relative {{ inline_admin_formset.classes }}" aria-labelledby="{{ inline_admin_formset.formset.prefix }}-heading">
|
|
5
5
|
{% if inline_admin_formset.is_collapsible %}<details><summary>{% endif %}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
{% if inline_admin_formset.formset.max_num == 1 %}
|
|
9
|
-
{{ inline_admin_formset.opts.verbose_name|capfirst }}
|
|
10
|
-
{% else %}
|
|
11
|
-
{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}
|
|
12
|
-
{% endif %}
|
|
13
|
-
</h2>
|
|
7
|
+
{% include "unfold/helpers/inline_heading.html" %}
|
|
14
8
|
|
|
15
9
|
{% if inline_admin_formset.is_collapsible %}</summary>{% endif %}
|
|
16
10
|
|
|
@@ -7,13 +7,7 @@
|
|
|
7
7
|
<fieldset class="module relative {{ inline_admin_formset.classes }} min-w-0" aria-labelledby="{{ inline_admin_formset.formset.prefix }}-heading">
|
|
8
8
|
{% if inline_admin_formset.is_collapsible %}<details><summary>{% endif %}
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
{% if inline_admin_formset.formset.max_num == 1 %}
|
|
12
|
-
{{ inline_admin_formset.opts.verbose_name|capfirst }}
|
|
13
|
-
{% else %}
|
|
14
|
-
{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}
|
|
15
|
-
{% endif %}
|
|
16
|
-
</h2>
|
|
10
|
+
{% include "unfold/helpers/inline_heading.html" %}
|
|
17
11
|
|
|
18
12
|
{% if inline_admin_formset.is_collapsible %}</summary>{% endif %}
|
|
19
13
|
|
|
@@ -9,9 +9,7 @@
|
|
|
9
9
|
{{ fieldset.name }}
|
|
10
10
|
</h2>
|
|
11
11
|
{% else %}
|
|
12
|
-
|
|
13
|
-
{{ fieldset.name }}
|
|
14
|
-
</h2>
|
|
12
|
+
{% include "unfold/helpers/inline_heading.html" with title=fieldset.name clickable=fieldset.is_collapsible %}
|
|
15
13
|
{% endif %}
|
|
16
14
|
{% endif %}
|
|
17
15
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
<input type="text"
|
|
12
12
|
x-ref="searchInput"
|
|
13
|
-
x-on:keydown.window="applyShortcut($event)"
|
|
13
|
+
{% if not cl.is_dataset %}x-on:keydown.window="applyShortcut($event)"{% endif %}
|
|
14
14
|
class="grow font-medium min-w-0 overflow-hidden p-2 placeholder-font-subtle-light truncate focus:outline-hidden dark:bg-base-900 dark:placeholder-font-subtle-dark dark:text-font-default-dark"
|
|
15
15
|
name="{{ search_var }}"
|
|
16
16
|
value="{{ cl.query }}"
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{% extends "admin/base_site.html" %}
|
|
2
2
|
{% load admin_urls i18n static %}
|
|
3
3
|
|
|
4
|
-
{% block extrastyle %}{{ block.super }}
|
|
4
|
+
{% block extrastyle %}{{ block.super }}{% endblock %}
|
|
5
5
|
|
|
6
6
|
{% block content %}
|
|
7
7
|
<div id="content-main">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
</p>
|
|
8
|
+
{% translate 'Your password was changed.' as message %}
|
|
9
|
+
{% include "unfold/helpers/messages/success.html" with message=message %}
|
|
11
10
|
</div>
|
|
12
11
|
{% endblock %}
|
|
@@ -19,15 +19,19 @@
|
|
|
19
19
|
|
|
20
20
|
{% include "unfold/helpers/messages/info.html" with message=message %}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
<fieldset class="fieldset group border border-base-200 mb-6 rounded-default shadow-xs p-3 dark:border-base-800 *:last:mb-0">
|
|
23
|
+
{% include "unfold/helpers/field.html" with field=form.old_password %}
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
{% include "unfold/helpers/field.html" with field=form.new_password1 %}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
{% include "unfold/helpers/field.html" with field=form.new_password2 %}
|
|
28
|
+
</fieldset>
|
|
27
29
|
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
<div class="flex justify-end">
|
|
31
|
+
<button type="submit" class="bg-primary-600 border border-transparent font-medium px-3 py-2 rounded-default text-sm text-white">
|
|
32
|
+
{% translate 'Change password' %}
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
31
35
|
</div>
|
|
32
36
|
</form>
|
|
33
37
|
</div>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
{% if actions_on_top %}
|
|
4
4
|
{% if cl.search_fields or action_form or cl.has_filters %}
|
|
5
5
|
<div class="bottom-0 left-0 right-0 hidden bg-primary-600 group-has-[input.action-select:checked]:flex fixed gap-3 lg:h-[64px] items-center p-3 z-50 lg:flex-row dark:bg-primary-500">
|
|
6
|
-
<div id="changelist-actions-wrapper" class="grow" {% if not is_popup %}x-bind:class="{'xl:ml-0': !sidebarDesktopOpen, 'xl:ml-72': sidebarDesktopOpen}"{% endif %}>
|
|
6
|
+
<div id="changelist-actions-wrapper" class="grow group changelist-actions" {% if not is_popup %}x-bind:class="{'xl:ml-0': !sidebarDesktopOpen, 'xl:ml-72': sidebarDesktopOpen}"{% endif %}>
|
|
7
7
|
{% if action_form %}
|
|
8
8
|
{% admin_actions %}
|
|
9
9
|
{% endif %}
|
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
{% load admin_list unfold_list %}
|
|
2
2
|
|
|
3
|
-
<div {% if
|
|
4
|
-
{% if not
|
|
5
|
-
|
|
6
|
-
{{ dataset.model_verbose_name|capfirst }}
|
|
7
|
-
</h2>
|
|
3
|
+
<div class="result-list-wrapper {% if not tab %}mt-6{% endif %}" x-data="{action: '', selectAcross: '0'}" {% if tab %}x-show="activeTab == 'dataset-{{ dataset.model_name }}'"{% else %}x-show="activeTab == 'general'"{% endif %}>
|
|
4
|
+
{% if not tab %}
|
|
5
|
+
{% include "unfold/helpers/inline_heading.html" with title=dataset.model_verbose_name %}
|
|
8
6
|
{% endif %}
|
|
9
7
|
|
|
10
8
|
{% if cl.search_fields %}
|
|
11
|
-
<div class="flex flex-col gap-4 mb-4 sm:flex-row empty:hidden lg:border lg:border-base-200 lg:dark:border-base-800 lg:-mb-8 lg:p-3 lg:pb-11 lg:rounded-t-default">
|
|
9
|
+
<div class="flex flex-col gap-4 justify-between mb-4 sm:flex-row empty:hidden lg:border lg:border-base-200 lg:dark:border-base-800 lg:-mb-8 lg:p-3 lg:pb-11 lg:rounded-t-default">
|
|
12
10
|
{% unfold_search_form cl %}
|
|
11
|
+
|
|
12
|
+
{% unfold_admin_actions %}
|
|
13
13
|
</div>
|
|
14
14
|
{% endif %}
|
|
15
15
|
|
|
16
|
-
{%
|
|
16
|
+
<form id="dataset-{{ id }}" class="group" method="post"{% if cl.formset and cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %} novalidate>
|
|
17
|
+
{% csrf_token %}
|
|
18
|
+
|
|
19
|
+
<input type="hidden" name="dataset" value="{{ id }}">
|
|
20
|
+
<input type="hidden" name="action" x-model="action">
|
|
21
|
+
<input type="hidden" name="select_across" x-model="selectAcross">
|
|
22
|
+
|
|
23
|
+
{% if cl.formset %}
|
|
24
|
+
{{ cl.formset.management_form }}
|
|
25
|
+
{% endif %}
|
|
26
|
+
|
|
27
|
+
{% unfold_result_list cl %}
|
|
28
|
+
</form>
|
|
17
29
|
|
|
18
30
|
{% pagination cl %}
|
|
19
31
|
</div>
|
|
@@ -2,23 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
{% with tabs=adminform|tabs %}
|
|
4
4
|
{% if tabs %}
|
|
5
|
-
<div x-data="{openTab: null}" x-show="activeTab == 'general'">
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<a
|
|
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 %} ? 'bg-white text-font-important-light shadow-xs dark:bg-base-900 dark:text-font-important-dark' : ''">
|
|
5
|
+
<div class="{% fieldset_rows_classes %}" x-data="{openTab: null}" x-show="activeTab == 'general'">
|
|
6
|
+
<div class="{% if adminform.model_admin.compressed_fields %}border-b border-base-200 border-dashed dark:border-base-800{% endif %}">
|
|
7
|
+
<nav class="bg-base-100 cursor-pointer flex flex-col font-medium gap-1 m-2 p-1 rounded-default text-important dark:border-base-700 md:inline-flex md:flex-row md:w-auto dark:bg-white/[.04] *:flex-inline *:flex-row *:items-center *:px-2.5 *:py-[5px] *:rounded-default *:transition-colors *:hover:bg-base-700/[.04] *:dark:hover:bg-white/[.04] [&>.active]:bg-white [&>.active]:shadow-xs [&>.active]:hover:bg-white [&>.active]:dark:bg-base-900 [&>.active]:dark:hover:bg-base-900">
|
|
8
|
+
{% for fieldset in tabs %}
|
|
9
|
+
<a x-on:click="openTab = '{{ forloop.counter0 }}-{{ fieldset.name|slugify }}'" x-bind:class="openTab == '{{ forloop.counter0 }}-{{ fieldset.name|slugify }}'{% if forloop.first %} || openTab == null{% endif %} ? 'active' : ''">
|
|
12
10
|
{{ fieldset.name }}
|
|
13
11
|
</a>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</
|
|
12
|
+
{% endfor %}
|
|
13
|
+
</nav>
|
|
14
|
+
</div>
|
|
17
15
|
|
|
18
16
|
{% for fieldset in tabs %}
|
|
19
17
|
<div class="tab-wrapper{% if fieldset.name %} fieldset-{{ fieldset.name|slugify }} fieldset-{{ forloop.counter0 }}-{{ fieldset.name|slugify }}{% endif %}"
|
|
20
18
|
x-show="openTab == '{{ forloop.counter0 }}-{{ fieldset.name|slugify }}'{% if forloop.first %} || openTab == null{% endif %}">
|
|
21
|
-
{% include 'admin/includes/fieldset.html' %}
|
|
19
|
+
{% include 'admin/includes/fieldset.html' with stacked=1 %}
|
|
22
20
|
</div>
|
|
23
21
|
{% endfor %}
|
|
24
22
|
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<h2 class="bg-base-100 flex flex-row font-semibold h-[38px] items-center mb-4 px-3 rounded-default text-important dark:bg-white/[.02] {% if inline_admin_formset.opts.tab %}hidden{% endif %} {% if clickable or inline_admin_formset.is_collapsible %}cursor-pointer{% endif %}" {% if inline_admin_formset %}id="{{ inline_admin_formset.formset.prefix }}-heading"{% endif %}>
|
|
2
|
+
{% if inline_admin_formset %}
|
|
3
|
+
{% if inline_admin_formset.formset.max_num == 1 %}
|
|
4
|
+
{{ inline_admin_formset.opts.verbose_name|capfirst }}
|
|
5
|
+
{% else %}
|
|
6
|
+
{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}
|
|
7
|
+
{% endif %}
|
|
8
|
+
{% else %}
|
|
9
|
+
{{ title|capfirst }}
|
|
10
|
+
{% endif %}
|
|
11
|
+
</h2>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{% load i18n %}
|
|
2
2
|
|
|
3
3
|
{% if inlines_list or tabs_list %}
|
|
4
|
-
<nav class="bg-base-100 flex flex-col font-medium gap-1 p-1 rounded-default w-full dark:border-base-700 md:flex-row md:w-auto dark:bg-white/[.04] *:flex *:flex-row *:font-medium *:items-center *:px-2.5 *:py-[5px] *:rounded-default *:transition-colors *:hover:bg-base-700/[.04] *:dark:hover:bg-white/[.04] [&>.active]:bg-white [&>.active]:shadow-xs [&>.active]:
|
|
4
|
+
<nav class="bg-base-100 flex flex-col font-medium gap-1 p-1 rounded-default text-important w-full dark:border-base-700 md:flex-row md:w-auto dark:bg-white/[.04] *:flex *:flex-row *:font-medium *:items-center *:px-2.5 *:py-[5px] *:rounded-default *:transition-colors *:hover:bg-base-700/[.04] *:dark:hover:bg-white/[.04] [&>.active]:bg-white [&>.active]:shadow-xs [&>.active]:hover:bg-white [&>.active]:dark:bg-base-900 [&>.active]:dark:hover:bg-base-900">
|
|
5
5
|
{% for item in tabs_list %}
|
|
6
6
|
{% if item.has_permission %}
|
|
7
7
|
<a href="{% if item.link_callback %}{{ item.link_callback }}{% else %}{{ item.link }}{% endif %}{% if item.inline %}#{{ item.inline|slugify }}{% endif %}" class="{% if item.active and not item.inline %}active{% endif %}" {% if item.inline %}x-on:click="activeTab = '{{ item.inline|slugify }}'" x-bind:class="{'active': activeTab == '{{ item.inline|slugify }}'}"{% endif %}>
|
|
@@ -26,9 +26,11 @@
|
|
|
26
26
|
{% endfor %}
|
|
27
27
|
|
|
28
28
|
{% for dataset in datasets_list %}
|
|
29
|
-
|
|
30
|
-
{{ dataset.
|
|
31
|
-
|
|
29
|
+
{% if dataset.tab %}
|
|
30
|
+
<a href="#dataset-{{ dataset.model_name }}" x-on:click="activeTab = 'dataset-{{ dataset.model_name }}'" x-bind:class="{'active': activeTab == 'dataset-{{ dataset.model_name }}'}">
|
|
31
|
+
{{ dataset.model_verbose_name|capfirst }}
|
|
32
|
+
</a>
|
|
33
|
+
{% endif %}
|
|
32
34
|
{% endfor %}
|
|
33
35
|
{% endif %}
|
|
34
36
|
</nav>
|
unfold/templatetags/unfold.py
CHANGED
|
@@ -151,76 +151,6 @@ def tabs(adminform: AdminForm) -> list[Fieldset]:
|
|
|
151
151
|
return result
|
|
152
152
|
|
|
153
153
|
|
|
154
|
-
class CaptureNode(Node):
|
|
155
|
-
def __init__(self, nodelist: NodeList, varname: str, silent: bool) -> None:
|
|
156
|
-
self.nodelist = nodelist
|
|
157
|
-
self.varname = varname
|
|
158
|
-
self.silent = silent
|
|
159
|
-
|
|
160
|
-
def render(self, context: dict[str, Any]) -> str | SafeText:
|
|
161
|
-
output = self.nodelist.render(context)
|
|
162
|
-
context[self.varname] = output
|
|
163
|
-
if self.silent:
|
|
164
|
-
return ""
|
|
165
|
-
else:
|
|
166
|
-
return output
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
@register.tag(name="capture")
|
|
170
|
-
def do_capture(parser: Parser, token: Token) -> CaptureNode:
|
|
171
|
-
"""
|
|
172
|
-
Capture the contents of a tag output.
|
|
173
|
-
Usage:
|
|
174
|
-
.. code-block:: html+django
|
|
175
|
-
{% capture %}..{% endcapture %} # output in {{ capture }}
|
|
176
|
-
{% capture silent %}..{% endcapture %} # output in {{ capture }} only
|
|
177
|
-
{% capture as varname %}..{% endcapture %} # output in {{ varname }}
|
|
178
|
-
{% capture as varname silent %}..{% endcapture %} # output in {{ varname }} only
|
|
179
|
-
For example:
|
|
180
|
-
.. code-block:: html+django
|
|
181
|
-
{# Allow templates to override the page title/description #}
|
|
182
|
-
<meta name="description" content="{% capture as meta_description %}
|
|
183
|
-
{% block meta-description %}{% endblock %}{% endcapture %}" />
|
|
184
|
-
<title>{% capture as meta_title %}{% block meta-title %}Untitled{% endblock %}{% endcapture %}</title>
|
|
185
|
-
{# copy the values to the Social Media meta tags #}
|
|
186
|
-
<meta property="og:description" content="{% block og-description %}{{ meta_description }}{% endblock %}" />
|
|
187
|
-
<meta name="twitter:title" content="{% block twitter-title %}{{ meta_title }}{% endblock %}" />
|
|
188
|
-
"""
|
|
189
|
-
bits = token.split_contents()
|
|
190
|
-
|
|
191
|
-
# tokens
|
|
192
|
-
t_as = "as"
|
|
193
|
-
t_silent = "silent"
|
|
194
|
-
var = "capture"
|
|
195
|
-
silent = False
|
|
196
|
-
|
|
197
|
-
num_bits = len(bits)
|
|
198
|
-
if len(bits) > 4:
|
|
199
|
-
raise TemplateSyntaxError(
|
|
200
|
-
"'capture' node supports '[as variable] [silent]' parameters."
|
|
201
|
-
)
|
|
202
|
-
elif num_bits == 4:
|
|
203
|
-
t_name, t_as, var, t_silent = bits
|
|
204
|
-
silent = True
|
|
205
|
-
elif num_bits == 3:
|
|
206
|
-
t_name, t_as, var = bits
|
|
207
|
-
elif num_bits == 2:
|
|
208
|
-
t_name, t_silent = bits
|
|
209
|
-
silent = True
|
|
210
|
-
else:
|
|
211
|
-
var = "capture"
|
|
212
|
-
silent = False
|
|
213
|
-
|
|
214
|
-
if t_silent != "silent" or t_as != "as":
|
|
215
|
-
raise TemplateSyntaxError(
|
|
216
|
-
"'capture' node expects 'as variable' or 'silent' syntax."
|
|
217
|
-
)
|
|
218
|
-
|
|
219
|
-
nodelist = parser.parse(("endcapture",))
|
|
220
|
-
parser.delete_first_token()
|
|
221
|
-
return CaptureNode(nodelist, var, silent)
|
|
222
|
-
|
|
223
|
-
|
|
224
154
|
class RenderComponentNode(template.Node):
|
|
225
155
|
def __init__(
|
|
226
156
|
self,
|
|
@@ -799,3 +729,50 @@ def has_nested_tables(table: dict) -> bool:
|
|
|
799
729
|
return any(
|
|
800
730
|
isinstance(row, dict) and "table" in row for row in table.get("rows", [])
|
|
801
731
|
)
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
class RenderCaptureNode(Node):
|
|
735
|
+
def __init__(self, nodelist: NodeList, variable_name: str, silent: bool) -> None:
|
|
736
|
+
self.nodelist = nodelist
|
|
737
|
+
self.variable_name = variable_name
|
|
738
|
+
self.silent = silent
|
|
739
|
+
|
|
740
|
+
def render(self, context: dict[str, Any]) -> str | SafeText:
|
|
741
|
+
content = self.nodelist.render(context)
|
|
742
|
+
|
|
743
|
+
if not self.silent:
|
|
744
|
+
return content
|
|
745
|
+
|
|
746
|
+
context.update(
|
|
747
|
+
{
|
|
748
|
+
self.variable_name: content,
|
|
749
|
+
}
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
return ""
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
@register.tag(name="capture")
|
|
756
|
+
def do_capture(parser: Parser, token: Token) -> RenderCaptureNode:
|
|
757
|
+
parts = token.split_contents()
|
|
758
|
+
variable_name = ""
|
|
759
|
+
silent = False
|
|
760
|
+
|
|
761
|
+
if len(parts) > 4:
|
|
762
|
+
raise TemplateSyntaxError("Too many arguments for 'capture' tag.")
|
|
763
|
+
|
|
764
|
+
if len(parts) >= 3:
|
|
765
|
+
if parts[1] != "as":
|
|
766
|
+
raise TemplateSyntaxError("'as' is required for 'capture' tag.")
|
|
767
|
+
|
|
768
|
+
variable_name = parts[2]
|
|
769
|
+
|
|
770
|
+
if len(parts) == 4:
|
|
771
|
+
if parts[3] != "silent":
|
|
772
|
+
raise TemplateSyntaxError("'silent' is required for 'capture' tag.")
|
|
773
|
+
|
|
774
|
+
silent = True
|
|
775
|
+
|
|
776
|
+
nodelist = parser.parse(("endcapture",))
|
|
777
|
+
parser.delete_first_token()
|
|
778
|
+
return RenderCaptureNode(nodelist, variable_name, silent)
|
|
@@ -5,6 +5,7 @@ from typing import Any
|
|
|
5
5
|
from django.contrib.admin.templatetags.admin_list import (
|
|
6
6
|
ResultList,
|
|
7
7
|
_coerce_field_name,
|
|
8
|
+
admin_actions,
|
|
8
9
|
result_hidden_fields,
|
|
9
10
|
)
|
|
10
11
|
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
|
|
@@ -126,6 +127,7 @@ def result_headers(cl):
|
|
|
126
127
|
"text": UnfoldBooleanWidget(
|
|
127
128
|
{
|
|
128
129
|
"id": "action-toggle",
|
|
130
|
+
"class": "action-toggle",
|
|
129
131
|
"aria-label": _(
|
|
130
132
|
"Select all objects on this page for an action"
|
|
131
133
|
),
|
|
@@ -471,3 +473,13 @@ def unfold_search_form_tag(parser, token):
|
|
|
471
473
|
template_name="search_form.html",
|
|
472
474
|
takes_context=False,
|
|
473
475
|
)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
@register.tag(name="unfold_admin_actions")
|
|
479
|
+
def unfold_admin_actions_tag(parser, token):
|
|
480
|
+
return InclusionAdminNode(
|
|
481
|
+
parser,
|
|
482
|
+
token,
|
|
483
|
+
func=admin_actions,
|
|
484
|
+
template_name="dataset_actions.html",
|
|
485
|
+
)
|
unfold/widgets.py
CHANGED
|
@@ -176,7 +176,7 @@ CHECKBOX_CLASSES = [
|
|
|
176
176
|
"focus:outline-offset-2",
|
|
177
177
|
"focus:outline-primary-500",
|
|
178
178
|
"after:absolute",
|
|
179
|
-
"after:content-['
|
|
179
|
+
r"after:content-['check\_small']",
|
|
180
180
|
"after:flex!",
|
|
181
181
|
"after:h-4",
|
|
182
182
|
"after:items-center",
|
|
@@ -185,7 +185,6 @@ CHECKBOX_CLASSES = [
|
|
|
185
185
|
"after:material-symbols-outlined",
|
|
186
186
|
"after:-ml-px",
|
|
187
187
|
"after:-mt-px",
|
|
188
|
-
"after:text-sm!",
|
|
189
188
|
"after:text-white",
|
|
190
189
|
"after:transition-all",
|
|
191
190
|
"after:w-4",
|
|
File without changes
|
|
File without changes
|