django-unfold 0.53.0__py3-none-any.whl → 0.55.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.53.0.dist-info → django_unfold-0.55.0.dist-info}/METADATA +24 -11
- {django_unfold-0.53.0.dist-info → django_unfold-0.55.0.dist-info}/RECORD +56 -33
- {django_unfold-0.53.0.dist-info → django_unfold-0.55.0.dist-info}/WHEEL +1 -1
- unfold/contrib/filters/admin/__init__.py +17 -0
- unfold/contrib/filters/admin/choice_filters.py +173 -0
- unfold/contrib/filters/admin/dropdown_filters.py +15 -1
- unfold/contrib/filters/admin/mixins.py +25 -0
- unfold/contrib/filters/forms.py +41 -3
- unfold/contrib/filters/templates/unfold/filters/filters_date_range.html +1 -1
- unfold/contrib/filters/templates/unfold/filters/filters_datetime_range.html +1 -1
- unfold/layout.py +23 -0
- unfold/sites.py +17 -6
- unfold/static/admin/js/inlines.js +23 -3
- unfold/static/unfold/css/styles.css +1 -1
- unfold/static/unfold/fonts/material-symbols/LICENSE +202 -0
- unfold/static/unfold/fonts/material-symbols/Material-Symbols-Outlined.woff2 +0 -0
- unfold/static/unfold/js/alpine.js +2 -2
- unfold/static/unfold/js/select2.init.js +4 -0
- unfold/styles.css +2 -1
- unfold/templates/admin/change_list.html +15 -13
- unfold/templates/admin/change_list_results.html +1 -1
- unfold/templates/admin/edit_inline/tabular.html +1 -2
- unfold/templates/unfold/components/button.html +1 -1
- unfold/templates/unfold/components/chart/cohort.html +2 -2
- unfold/templates/unfold/helpers/change_list_filter.html +2 -2
- unfold/templates/unfold/helpers/change_list_filter_actions.html +28 -26
- unfold/templates/unfold/helpers/edit_inline/tabular_field.html +10 -8
- unfold/templates/unfold/helpers/field.html +4 -2
- unfold/templates/unfold/helpers/form_label.html +1 -1
- unfold/templates/unfold/helpers/tab_action.html +17 -2
- unfold/templates/unfold/widgets/clearable_file_input.html +1 -1
- unfold/templates/unfold/widgets/clearable_file_input_small.html +1 -1
- unfold/templates/unfold/widgets/radio_option.html +1 -1
- unfold/templates/unfold_crispy/display_form.html +9 -0
- unfold/templates/unfold_crispy/errors.html +3 -0
- unfold/templates/unfold_crispy/field.html +23 -0
- unfold/templates/unfold_crispy/inputs.html +7 -0
- unfold/templates/unfold_crispy/layout/attrs.html +1 -0
- unfold/templates/unfold_crispy/layout/baseinput.html +9 -0
- unfold/templates/unfold_crispy/layout/button.html +1 -0
- unfold/templates/unfold_crispy/layout/buttonholder.html +3 -0
- unfold/templates/unfold_crispy/layout/checkbox.html +19 -0
- unfold/templates/unfold_crispy/layout/column.html +3 -0
- unfold/templates/unfold_crispy/layout/div.html +4 -0
- unfold/templates/unfold_crispy/layout/field_errors.html +7 -0
- unfold/templates/unfold_crispy/layout/fieldset.html +9 -0
- unfold/templates/unfold_crispy/layout/help_text.html +5 -0
- unfold/templates/unfold_crispy/layout/help_text_and_errors.html +3 -0
- unfold/templates/unfold_crispy/layout/radio_checkbox_select.html +21 -0
- unfold/templates/unfold_crispy/layout/row.html +3 -0
- unfold/templates/unfold_crispy/layout/table_inline_formset.html +100 -0
- unfold/templates/unfold_crispy/uni_form.html +11 -0
- unfold/templates/unfold_crispy/whole_uni_form.html +14 -0
- unfold/templatetags/unfold.py +26 -14
- unfold/widgets.py +88 -1
- {django_unfold-0.53.0.dist-info → django_unfold-0.55.0.dist-info}/LICENSE.md +0 -0
@@ -1,31 +1,33 @@
|
|
1
1
|
{% load i18n %}
|
2
2
|
|
3
|
-
|
4
|
-
{% if cl.model_admin.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
{% if cl.model_admin.list_filter_submit or cl.is_facets_optional or cl.has_active_filters %}
|
4
|
+
<div class="bg-white border-t border-base-200 p-3 py-2.5 dark:bg-base-800 dark:border-base-700{% if not cl.model_admin.list_filter_sheet %} 2xl:!border-t-0 2xl:!bg-transparent 2xl:px-0{% endif %}">
|
5
|
+
{% if cl.model_admin.list_filter_submit %}
|
6
|
+
<button type="submit" class="bg-primary-600 block border border-transparent font-medium px-3 py-2 rounded text-white w-full">
|
7
|
+
{% trans "Apply Filters" %}
|
8
|
+
</button>
|
9
|
+
{% endif %}
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
{% if cl.is_facets_optional or cl.has_active_filters %}
|
12
|
+
<span id="changelist-filter-extra-actions" class="flex flex-row gap-2 items-center mt-2">
|
13
|
+
{% if cl.is_facets_optional %}
|
14
|
+
{% if cl.add_facets %}
|
15
|
+
<a href="{{ cl.remove_facet_link }}" class="hidelink border flex-grow font-medium px-3 py-2 rounded text-center transition-all w-full hover:bg-base-50 lg:w-auto dark:border-base-700 dark:hover:text-base-200">
|
16
|
+
{% trans "Hide counts" %}
|
17
|
+
</a>
|
18
|
+
{% else %}
|
19
|
+
<a href="{{ cl.add_facet_link }}" class="viewlink border flex-grow font-medium px-3 py-2 rounded text-center transition-all w-full lg:w-auto dark:border-base-700 dark:hover:text-base-200">
|
20
|
+
{% trans "Show counts" %}
|
21
|
+
</a>
|
22
|
+
{% endif %}
|
21
23
|
{% endif %}
|
22
|
-
{% endif %}
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
</div>
|
25
|
+
{% if cl.has_active_filters %}
|
26
|
+
<a href="{{ cl.clear_all_filters_qs }}" class="border flex-grow font-medium px-3 py-2 rounded text-center transition-all w-full lg:w-auto dark:border-base-700 dark:hover:text-base-200">
|
27
|
+
{% trans "Clear all filters" %}
|
28
|
+
</a>
|
29
|
+
{% endif %}
|
30
|
+
</span>
|
31
|
+
{% endif %}
|
32
|
+
</div>
|
33
|
+
{% endif %}
|
@@ -12,14 +12,16 @@
|
|
12
12
|
{% if field.is_readonly %}
|
13
13
|
{% include "unfold/helpers/field_readonly_value.html" with tabular=1 %}
|
14
14
|
{% else %}
|
15
|
-
|
15
|
+
<div class="flex flex-col">
|
16
|
+
{{ field.field }}
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
{% if field.field.errors|length > 0 %}
|
19
|
+
<div class="mt-1 text-red-600 text-sm dark:text-red-500">
|
20
|
+
{% for error in field.field.errors %}
|
21
|
+
{{ error }}
|
22
|
+
{% endfor %}
|
23
|
+
</div>
|
24
|
+
{% endif %}
|
25
|
+
</div>
|
24
26
|
{% endif %}
|
25
27
|
</div>
|
@@ -1,4 +1,6 @@
|
|
1
|
-
{%
|
1
|
+
{% load unfold %}
|
2
|
+
|
3
|
+
{% if field.field.widget.input_type == "checkbox" and field.field.widget|class_name != "UnfoldAdminCheckboxSelectMultiple"%}
|
2
4
|
<div class="{% if field.errors %}errors {% endif %}flex flex-col group mb-6 last:mb-4">
|
3
5
|
<div class="flex flex-row gap-2 items-center">
|
4
6
|
{{ field }}
|
@@ -11,7 +13,7 @@
|
|
11
13
|
{% include "unfold/helpers/help_text.html" with help_text=field.help_text %}
|
12
14
|
</div>
|
13
15
|
{% else %}
|
14
|
-
<div class="{% if field.errors %}errors {% endif %}flex flex-col group mb-6 last:mb-4">
|
16
|
+
<div class="{% if field.errors %}errors {% endif %}flex flex-col gap-2 group mb-6 last:mb-4">
|
15
17
|
{% include "unfold/helpers/form_label.html" with field=field %}
|
16
18
|
|
17
19
|
{{ field }}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<label for="{{ field.id_for_label }}" class="block text-font-important-light dark:text-font-important-dark
|
1
|
+
<label for="{{ field.id_for_label }}" class="block font-semibold text-font-important-light dark:text-font-important-dark">
|
2
2
|
{{ field.label }}
|
3
3
|
|
4
4
|
{% if field.field.required %}
|
@@ -15,8 +15,23 @@
|
|
15
15
|
{{ title }}
|
16
16
|
|
17
17
|
{% if not link %}
|
18
|
-
<span class="
|
19
|
-
|
18
|
+
<span class="border-l border-base-200 flex items-center select-none self-stretch ml-1 -mr-1 -my-2 pl-2
|
19
|
+
{% if action.variant.value == "primary" %}
|
20
|
+
border-primary-700 dark:border-primary-500
|
21
|
+
{% elif action.variant.value == "danger" %}
|
22
|
+
border-red-700 dark:border-red-500
|
23
|
+
{% elif action.variant.value == "success" %}
|
24
|
+
border-green-700 dark:border-green-500
|
25
|
+
{% elif action.variant.value == "info" %}
|
26
|
+
border-blue-700 dark:border-blue-500
|
27
|
+
{% elif action.variant.value == "warning" %}
|
28
|
+
border-orange-700 dark:border-orange-500
|
29
|
+
{% else %}
|
30
|
+
border-base-200 dark:border-base-700
|
31
|
+
{% endif %}">
|
32
|
+
<span class="material-symbols-outlined ml-auto rotate-90 transition-transform" x-bind:class="{'!-rotate-90': actionDropdownOpen }">
|
33
|
+
chevron_right
|
34
|
+
</span>
|
20
35
|
</span>
|
21
36
|
{% endif %}
|
22
37
|
</a>
|
@@ -8,7 +8,7 @@
|
|
8
8
|
</div>
|
9
9
|
{% endif %}
|
10
10
|
|
11
|
-
<div class="
|
11
|
+
<div class="{{ widget.file_wrapper_class }}">
|
12
12
|
{% if widget.is_initial and not widget.required %}
|
13
13
|
<div class="bg-base-50 border-r border-base-200 flex flex-none items-center self-stretch px-3 dark:bg-white/[.02] dark:border-base-700 dark:text-font-default-dark">
|
14
14
|
<label for="{{ widget.checkbox_id }}" class="flex items-center">
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{% load i18n static %}
|
2
2
|
|
3
3
|
<div class="flex flex-row">
|
4
|
-
<div class="
|
4
|
+
<div class="{{ widget.file_wrapper_class }}">
|
5
5
|
{% if widget.is_initial and not widget.required %}
|
6
6
|
<div class="bg-base-50 border-r border-base-200 flex flex-none items-center self-stretch px-3 dark:bg-base-900 dark:border-r-gray-700">
|
7
7
|
<label for="{{ widget.checkbox_id }}" class="flex items-center">
|
@@ -0,0 +1,23 @@
|
|
1
|
+
{% load crispy_forms_field unfold %}
|
2
|
+
|
3
|
+
{% if field.is_hidden %}
|
4
|
+
{{ field }}
|
5
|
+
{% else %}
|
6
|
+
<{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="group {% if tag == "td" %}align-top border-t border-base-200 font-normal gap-4 min-w-0 overflow-hidden px-3 py-3 text-left dark:border-base-800 dark:before:text-font-important-dark{% endif%} {% if field.errors %}errors{% endif %} {% if field_class %} {{ field_class }}{% endif %} {% if field|is_checkbox and tag == "td" %}flex flex-row gap-2 h-9.5 items-center{% else %}{% if 'form-horizontal' in form_class %} row{% endif %}{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
|
7
|
+
{% if field.label and not field|is_checkbox and form_show_labels %}
|
8
|
+
<label {% if field.id_for_label %}for="{{ field.id_for_label }}"{% endif %} class="block font-semibold mb-2 text-font-important-light text-sm dark:text-font-important-dark {% if label_class %} {{ label_class }}{% endif %}">
|
9
|
+
{{ field.label }}{% if field.field.required %} <span class="asteriskField">*</span>{% endif %}
|
10
|
+
</label>
|
11
|
+
{% endif %}
|
12
|
+
|
13
|
+
{% if field|is_checkboxselectmultiple or field|is_radioselect %}
|
14
|
+
{% include 'unfold_crispy/layout/radio_checkbox_select.html' %}
|
15
|
+
{% elif field|is_checkbox %}
|
16
|
+
{% include 'unfold_crispy/layout/checkbox.html' %}
|
17
|
+
{% else %}
|
18
|
+
{% crispy_field field %}
|
19
|
+
|
20
|
+
{% include 'unfold_crispy/layout/help_text_and_errors.html' %}
|
21
|
+
{% endif %}
|
22
|
+
</{% if tag %}{{ tag }}{% else %}div{% endif %}>
|
23
|
+
{% endif %}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
{% if inputs %}
|
2
|
+
<div class="border border-base-200 rounded flex flex-col gap-2 justify-end p-3 lg:flex-row dark:border-base-800 {{ field_class }}">
|
3
|
+
{% for input in inputs %}
|
4
|
+
{% include "unfold_crispy/layout/baseinput.html" %}
|
5
|
+
{% endfor %}
|
6
|
+
</div>
|
7
|
+
{% endif %}
|
@@ -0,0 +1 @@
|
|
1
|
+
{% for name, value in widget.attrs.items %}{% if value is not False %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<input type="{{ input.input_type }}"
|
2
|
+
name="{% if input.name|wordcount > 1 %}{{ input.name|slugify }}{% else %}{{ input.name }}{% endif %}"
|
3
|
+
value="{{ input.value }}"
|
4
|
+
{% if input.input_type != "hidden" %}
|
5
|
+
class="{{ input.field_classes }}"
|
6
|
+
id="{% if input.id %}{{ input.id }}{% else %}{{ input.input_type }}-id-{{ input.name|slugify }}{% endif %}"
|
7
|
+
{% endif %}
|
8
|
+
{{ input.flat_attrs }}
|
9
|
+
/>
|
@@ -0,0 +1 @@
|
|
1
|
+
<button {{ button.flat_attrs }}>{{ button.content|safe }}</button>
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<div {% if buttonholder.css_id %}id="{{ buttonholder.css_id }}"{% endif %} class="buttonHolder group-[.fieldset]:border-t group-[.fieldset]:border-base-200 group-[.fieldset]:flex group-[.fieldset]:flex-row group-[.fieldset]:justify-end group-[.fieldset]:-mx-3 group-[.fieldset]:pt-3 group-[.fieldset]:px-3{% if buttonholder.css_class %} {{ buttonholder.css_class }}{% endif %}">
|
2
|
+
{{ fields_output|safe }}
|
3
|
+
</div>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{% load crispy_forms_field unfold %}
|
2
|
+
|
3
|
+
{% if form_show_labels %}
|
4
|
+
<label for="{{ field.id_for_label }}" class="flex flex-row gap-3 items-center">
|
5
|
+
{% crispy_field field 'class' form_classes.switch %} <span>{{ field.label }}{% if field.field.required %} <span class="asteriskField">*</span>{% endif %}</span>
|
6
|
+
</label>
|
7
|
+
|
8
|
+
{% include 'unfold_crispy/layout/help_text_and_errors.html' %}
|
9
|
+
{% else %}
|
10
|
+
{% with field=field|add_css_class:form_classes.checkbox %}
|
11
|
+
{% if tag == "td" %}
|
12
|
+
<div class="flex items-center justify-center h-9.5">
|
13
|
+
{% crispy_field field %}
|
14
|
+
</div>
|
15
|
+
{% else %}
|
16
|
+
{% crispy_field field %}
|
17
|
+
{% endif %}
|
18
|
+
{% endwith %}
|
19
|
+
{% endif %}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<fieldset {% if fieldset.css_id %}id="{{ fieldset.css_id }}"{% endif %} class="fieldset group flex flex-col gap-5 grow rounded border-base-200 shadow-sm aligned border p-3 relative dark:border-base-800 {% if fieldset.css_class %} {{ fieldset.css_class }}{% endif %}" {{ fieldset.flat_attrs }}>
|
2
|
+
{% if legend %}
|
3
|
+
<legend class="border-b border-base-200 font-semibold float-left pb-3 -mx-3 px-3 text-font-important-light dark:text-font-important-dark dark:border-base-800">
|
4
|
+
{{ legend|safe }}
|
5
|
+
</legend>
|
6
|
+
{% endif %}
|
7
|
+
|
8
|
+
{{ fields|safe }}
|
9
|
+
</fieldset>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
{% load crispy_forms_filters l10n %}
|
2
|
+
|
3
|
+
<div class="flex flex-col gap-2 {% if field_class %}{{ field_class }}{% endif %}"{% if flat_attrs %} {{ flat_attrs }}{% endif %}>
|
4
|
+
{% for group, options, index in field|optgroups %}
|
5
|
+
{% if group %}
|
6
|
+
<strong>{{ group }}</strong>
|
7
|
+
{% endif %}
|
8
|
+
|
9
|
+
{% for option in options %}
|
10
|
+
<div>
|
11
|
+
<label for="{{ option.attrs.id }}" class="flex flex-row items-center gap-2">
|
12
|
+
<input type="{{ option.type }}" class="{% if field.errors %}errors{% endif %} {% if option.type == "radio" %}{{ form_classes.radio }}{% else %}{{ form_classes.checkbox }}{% endif %}" name="{{ field.html_name }}" value="{{ option.value|unlocalize }}" {% include "unfold_crispy/layout/attrs.html" with widget=option %}>
|
13
|
+
|
14
|
+
{{ option.label|unlocalize }}
|
15
|
+
</label>
|
16
|
+
</div>
|
17
|
+
{% endfor %}
|
18
|
+
{% endfor %}
|
19
|
+
</div>
|
20
|
+
|
21
|
+
{% include 'unfold_crispy/layout/help_text_and_errors.html' %}
|
@@ -0,0 +1,100 @@
|
|
1
|
+
{% load crispy_forms_tags crispy_forms_utils crispy_forms_field i18n %}
|
2
|
+
|
3
|
+
{% specialspaceless %}
|
4
|
+
{% if formset_tag %}
|
5
|
+
<form {{ flat_attrs }} method="{{ form_method }}" {% if formset.is_multipart %} enctype="multipart/form-data"{% endif %}>
|
6
|
+
{% endif %}
|
7
|
+
|
8
|
+
{% if formset_method|lower == 'post' and not disable_csrf %}
|
9
|
+
{% csrf_token %}
|
10
|
+
{% endif %}
|
11
|
+
|
12
|
+
<div class="overflow-x-auto border border-base-200 rounded shadow-sm dark:border-base-800" {% if form_id %} id="{{ form_id }}"{% endif %}>
|
13
|
+
{{ formset.management_form|crispy }}
|
14
|
+
|
15
|
+
<table class="w-full">
|
16
|
+
<thead>
|
17
|
+
{% if formset.readonly and not formset.queryset.exists %}
|
18
|
+
{% else %}
|
19
|
+
<tr>
|
20
|
+
{% for field in formset.forms.0 %}
|
21
|
+
{% if field.label and not field.is_hidden %}
|
22
|
+
<th for="{{ field.auto_id }}" class="align-middle font-semibold py-2 text-left text-font-important-light dark:text-font-important-dark whitespace-nowrap px-3 {% if field.name == "DELETE" %}w-0{% endif %}">
|
23
|
+
{{ field.label }}{% if field.field.required and not field|is_checkbox %} <span class="asteriskField">*</span>{% endif %}
|
24
|
+
</th>
|
25
|
+
{% endif %}
|
26
|
+
{% endfor %}
|
27
|
+
</tr>
|
28
|
+
{% endif %}
|
29
|
+
</thead>
|
30
|
+
|
31
|
+
<tbody {% if formset_id %}id="{{ formset_id }}-rows"{% endif %}>
|
32
|
+
{% for form in formset %}
|
33
|
+
{% if form_show_errors and form.non_field_errors and not form.is_extra %}
|
34
|
+
<tr>
|
35
|
+
<td colspan="100%" class="border-t border-base-200 px-3 pt-3 dark:border-base-800">
|
36
|
+
{% include "unfold_crispy/errors.html" %}
|
37
|
+
</td>
|
38
|
+
</tr>
|
39
|
+
{% endif %}
|
40
|
+
|
41
|
+
<tr>
|
42
|
+
{% for field in form %}
|
43
|
+
{% include 'unfold_crispy/field.html' with tag="td" form_show_labels=False %}
|
44
|
+
{% endfor %}
|
45
|
+
</tr>
|
46
|
+
{% endfor %}
|
47
|
+
</tbody>
|
48
|
+
|
49
|
+
{% if form_add and formset_id %}
|
50
|
+
<tbody>
|
51
|
+
<tr class="empty-form">
|
52
|
+
{% for field in formset.empty_form %}
|
53
|
+
{% include 'unfold_crispy/field.html' with tag="td" form_show_labels=False %}
|
54
|
+
{% endfor %}
|
55
|
+
</tr>
|
56
|
+
|
57
|
+
<tr id="{{ formset_id }}-add-row-wrapper">
|
58
|
+
<td colspan="100%" class="border-t border-base-200 p-3 dark:border-base-800">
|
59
|
+
<div class="flex justify-end">
|
60
|
+
<button type="button" id="{{ formset_id }}-add-row-button" class="border border-base-200 font-medium px-3 py-2 rounded transition-all w-full hover:bg-base-50 lg:block lg:w-auto dark:border-base-700 dark:hover:text-base-200 dark:hover:bg-base-900">
|
61
|
+
{% trans "Add row" %}
|
62
|
+
</button>
|
63
|
+
</div>
|
64
|
+
</td>
|
65
|
+
</tr>
|
66
|
+
</tbody>
|
67
|
+
{% endif %}
|
68
|
+
</table>
|
69
|
+
|
70
|
+
{% include "unfold_crispy/inputs.html" %}
|
71
|
+
</div>
|
72
|
+
|
73
|
+
{% if formset_tag %}
|
74
|
+
</form>
|
75
|
+
{% endif %}
|
76
|
+
|
77
|
+
{% if form_add and formset_id %}
|
78
|
+
<script>
|
79
|
+
document.getElementById('{{ formset_id }}-add-row-button').addEventListener('click', function() {
|
80
|
+
const formTotalEl = document.querySelector(`#{{ formset_id }} input[name*="TOTAL_FORMS"]`)
|
81
|
+
const formCount = parseInt(formTotalEl.value);
|
82
|
+
const newForm = document.querySelector('#{{ formset_id }} .empty-form').cloneNode(true);
|
83
|
+
|
84
|
+
newForm.classList.remove('empty-form');
|
85
|
+
newForm.innerHTML = newForm.innerHTML.replaceAll(/__prefix__/g, formCount);
|
86
|
+
|
87
|
+
document.getElementById('{{ formset_id }}-rows').insertBefore(
|
88
|
+
newForm,
|
89
|
+
document.getElementById("#{{ formset_id }}-add-row-wrapper")
|
90
|
+
)
|
91
|
+
|
92
|
+
formTotalEl.value = formCount + 1;
|
93
|
+
|
94
|
+
newForm.dispatchEvent(new CustomEvent('formset:added', {
|
95
|
+
bubbles: true
|
96
|
+
}));
|
97
|
+
});
|
98
|
+
</script>
|
99
|
+
{% endif %}
|
100
|
+
{% endspecialspaceless %}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
{% load crispy_forms_utils %}
|
2
|
+
|
3
|
+
{% specialspaceless %}
|
4
|
+
{% if include_media %}{{ form.media }}{% endif %}
|
5
|
+
{% if form_show_errors %}
|
6
|
+
{% include "unfold_crispy/errors.html" %}
|
7
|
+
{% endif %}
|
8
|
+
{% for field in form %}
|
9
|
+
{% include field_template %}
|
10
|
+
{% endfor %}
|
11
|
+
{% endspecialspaceless %}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{% load crispy_forms_utils %}
|
2
|
+
|
3
|
+
{% specialspaceless %}
|
4
|
+
{% if form_tag %}<form {{ flat_attrs }} method="{{ form_method }}" {% if form.is_multipart %} enctype="multipart/form-data"{% endif %}>{% endif %}
|
5
|
+
{% if form_method|lower == 'post' and not disable_csrf %}
|
6
|
+
{% csrf_token %}
|
7
|
+
{% endif %}
|
8
|
+
|
9
|
+
{% include "unfold_crispy/display_form.html" %}
|
10
|
+
|
11
|
+
{% include "unfold_crispy/inputs.html" %}
|
12
|
+
|
13
|
+
{% if form_tag %}</form>{% endif %}
|
14
|
+
{% endspecialspaceless %}
|
unfold/templatetags/unfold.py
CHANGED
@@ -8,7 +8,7 @@ from django.contrib.admin.views.main import ChangeList
|
|
8
8
|
from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
|
9
9
|
from django.db.models import Model
|
10
10
|
from django.db.models.options import Options
|
11
|
-
from django.forms import BoundField, Field
|
11
|
+
from django.forms import BoundField, CheckboxSelectMultiple, Field
|
12
12
|
from django.http import HttpRequest
|
13
13
|
from django.template import Context, Library, Node, RequestContext, TemplateSyntaxError
|
14
14
|
from django.template.base import NodeList, Parser, Token, token_kwargs
|
@@ -18,7 +18,7 @@ from django.utils.safestring import SafeText, mark_safe
|
|
18
18
|
from unfold.components import ComponentRegistry
|
19
19
|
from unfold.dataclasses import UnfoldAction
|
20
20
|
from unfold.enums import ActionVariant
|
21
|
-
from unfold.widgets import UnfoldAdminSplitDateTimeWidget
|
21
|
+
from unfold.widgets import UnfoldAdminMoneyWidget, UnfoldAdminSplitDateTimeWidget
|
22
22
|
|
23
23
|
register = Library()
|
24
24
|
|
@@ -438,41 +438,46 @@ def action_item_classes(context: Context, action: UnfoldAction) -> str:
|
|
438
438
|
if variant == ActionVariant.PRIMARY:
|
439
439
|
classes.extend(
|
440
440
|
[
|
441
|
-
"border-primary-
|
441
|
+
"border-primary-700",
|
442
442
|
"bg-primary-600",
|
443
443
|
"text-white",
|
444
|
+
"dark:border-primary-500",
|
444
445
|
]
|
445
446
|
)
|
446
447
|
elif variant == ActionVariant.DANGER:
|
447
448
|
classes.extend(
|
448
449
|
[
|
449
|
-
"border-red-
|
450
|
+
"border-red-700",
|
450
451
|
"bg-red-600",
|
451
452
|
"text-white",
|
453
|
+
"dark:border-red-500",
|
452
454
|
]
|
453
455
|
)
|
454
456
|
elif variant == ActionVariant.SUCCESS:
|
455
457
|
classes.extend(
|
456
458
|
[
|
457
|
-
"border-green-
|
459
|
+
"border-green-700",
|
458
460
|
"bg-green-600",
|
459
461
|
"text-white",
|
462
|
+
"dark:border-green-500",
|
460
463
|
]
|
461
464
|
)
|
462
465
|
elif variant == ActionVariant.INFO:
|
463
466
|
classes.extend(
|
464
467
|
[
|
465
|
-
"border-blue-
|
468
|
+
"border-blue-700",
|
466
469
|
"bg-blue-600",
|
467
470
|
"text-white",
|
471
|
+
"dark:border-blue-500",
|
468
472
|
]
|
469
473
|
)
|
470
474
|
elif variant == ActionVariant.WARNING:
|
471
475
|
classes.extend(
|
472
476
|
[
|
473
|
-
"border-orange-
|
477
|
+
"border-orange-700",
|
474
478
|
"bg-orange-600",
|
475
479
|
"text-white",
|
480
|
+
"dark:border-orange-500",
|
476
481
|
]
|
477
482
|
)
|
478
483
|
else:
|
@@ -480,6 +485,7 @@ def action_item_classes(context: Context, action: UnfoldAction) -> str:
|
|
480
485
|
[
|
481
486
|
"border-base-200",
|
482
487
|
"hover:text-primary-600",
|
488
|
+
"dark:hover:text-primary-500",
|
483
489
|
"dark:border-base-700",
|
484
490
|
]
|
485
491
|
)
|
@@ -489,7 +495,7 @@ def action_item_classes(context: Context, action: UnfoldAction) -> str:
|
|
489
495
|
|
490
496
|
@register.filter
|
491
497
|
def changeform_data(adminform: AdminForm) -> str:
|
492
|
-
fields =
|
498
|
+
fields = {}
|
493
499
|
|
494
500
|
for fieldset in adminform:
|
495
501
|
for line in fieldset:
|
@@ -497,15 +503,19 @@ def changeform_data(adminform: AdminForm) -> str:
|
|
497
503
|
if isinstance(field.field, dict):
|
498
504
|
continue
|
499
505
|
|
500
|
-
if isinstance(
|
506
|
+
if isinstance(
|
507
|
+
field.field.field.widget, UnfoldAdminSplitDateTimeWidget
|
508
|
+
) or isinstance(field.field.field.widget, UnfoldAdminMoneyWidget):
|
501
509
|
for index, _widget in enumerate(field.field.field.widget.widgets):
|
502
|
-
fields
|
510
|
+
fields[
|
503
511
|
f"{field.field.name}{field.field.field.widget.widgets_names[index]}"
|
504
|
-
|
512
|
+
] = None
|
513
|
+
elif isinstance(field.field.field.widget, CheckboxSelectMultiple):
|
514
|
+
fields[field.field.name] = []
|
505
515
|
else:
|
506
|
-
fields
|
516
|
+
fields[field.field.name] = None
|
507
517
|
|
508
|
-
return mark_safe(json.dumps(
|
518
|
+
return mark_safe(json.dumps(fields))
|
509
519
|
|
510
520
|
|
511
521
|
@register.filter(takes_context=True)
|
@@ -518,7 +528,9 @@ def changeform_condition(field: BoundField) -> BoundField:
|
|
518
528
|
field.field.field.widget.widget.attrs["x-init"] = mark_safe(
|
519
529
|
f"const $ = django.jQuery; $(function () {{ const select = $('#{field.field.auto_id}'); select.on('change', (ev) => {{ {field.field.name} = select.val(); }}); }});"
|
520
530
|
)
|
521
|
-
elif isinstance(
|
531
|
+
elif isinstance(
|
532
|
+
field.field.field.widget, UnfoldAdminSplitDateTimeWidget
|
533
|
+
) or isinstance(field.field.field.widget, UnfoldAdminMoneyWidget):
|
522
534
|
for index, widget in enumerate(field.field.field.widget.widgets):
|
523
535
|
field_name = (
|
524
536
|
f"{field.field.name}{field.field.field.widget.widgets_names[index]}"
|