django-unfold 0.50.0__py3-none-any.whl → 0.51.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.
@@ -1,4 +1,4 @@
1
- {% load admin_urls i18n %}
1
+ {% load admin_urls i18n unfold %}
2
2
 
3
3
  {% if result_hidden_fields %}
4
4
  <div class="hiddenfields">
@@ -11,6 +11,10 @@
11
11
  <table id="result_list" class="block border-base-200 border-spacing-none border-separate w-full lg:table">
12
12
  <thead>
13
13
  <tr>
14
+ {% if cl.model_admin.list_sections|length > 0 %}
15
+ <th></th>
16
+ {% endif %}
17
+
14
18
  {% for header in result_headers %}
15
19
  <th class="align-middle font-semibold py-2 text-left text-font-important-light dark:text-font-important-dark whitespace-nowrap {{ header.class_attrib }} {% if "action-toggle" in header.text and forloop.counter == 1 %}lg:px-3 lg:w-10{% else %}hidden px-3 lg:table-cell{% endif %}" scope="col">
16
20
  <div class="flex items-center">
@@ -65,8 +69,8 @@
65
69
  </tr>
66
70
  </thead>
67
71
 
68
- <tbody class="block lg:table-row-group">
69
- {% for result in results %}
72
+ {% for result in results %}
73
+ <tbody class="block lg:table-row-group" x-data="{rowOpen: false}">
70
74
  {% if result.form and result.form.non_field_errors %}
71
75
  <tr>
72
76
  <td class="text-left" colspan="{{ result|length }}">
@@ -76,13 +80,39 @@
76
80
  {% endif %}
77
81
 
78
82
  <tr class="{% cycle '' 'bg-base-50 dark:bg-white/[.02]' %} block border mb-3 rounded shadow-sm lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-base-800">
83
+ {% if cl.model_admin.list_sections|length > 0 %}
84
+ <td class="align-middle cursor-pointer flex border-b border-base-200 font-normal px-2.5 py-2 relative text-left before:font-semibold before:text-font-important-light before:block before:capitalize before:content-[attr(data-label)] before:mr-auto lg:before:hidden lg:border-b-0 lg:border-t lg:pl-3 lg:pr-0 lg:py-3 lg:table-cell dark:border-base-800 dark:lg:border-base-800 dark:before:text-font-important-dark lg:w-px"
85
+ data-label="{% trans "Expand row" %}"
86
+ x-on:click="rowOpen = !rowOpen">
87
+ <div class="absolute bg-primary-600 -bottom-px hidden left-0 top-0 w-0.5 z-10 lg:block" x-show="rowOpen"></div>
88
+ <span class="material-symbols-outlined select-none !block -rotate-90 transition-all" x-bind:class="rowOpen && 'rotate-0'">
89
+ expand_more
90
+ </span>
91
+ </td>
92
+ {% endif %}
93
+
79
94
  {% for item in result %}
80
95
  {{ item }}
81
96
  {% endfor %}
82
- {% include 'unfold/helpers/actions_row.html' with actions=actions_row instance_pk=result.instance_pk %}
97
+
98
+ {% include 'unfold/helpers/actions_row.html' with actions=actions_row instance_pk=result.instance.pk %}
83
99
  </tr>
84
- {% endfor %}
85
- </tbody>
100
+
101
+ {% if cl.model_admin.list_sections|length > 0 %}
102
+ <tr class="block mb-3 lg:table-row" x-show="rowOpen">
103
+ <td colspan="{{ result|length|add:1 }}" class="border bg-base-200/10 block border-base-200 relative rounded p-3 lg:shadow-inner lg:border-0 lg:border-t lg:rounded-none lg:table-cell dark:border-base-800">
104
+ <div class="absolute bg-primary-600 h-full hidden left-0 top-0 w-0.5 lg:block"></div>
105
+
106
+ <div class="grid gap-3 {{ cl.model_admin.list_sections_classes }}">
107
+ {% for section in cl.model_admin.list_sections %}
108
+ {% render_section cl.model_admin.list_sections|index:forloop.counter0 result.instance %}
109
+ {% endfor %}
110
+ </div>
111
+ </td>
112
+ </tr>
113
+ {% endif %}
114
+ </tbody>
115
+ {% endfor %}
86
116
  </table>
87
117
  </div>
88
118
  {% else %}
@@ -15,7 +15,7 @@
15
15
  <ul class="dark:bg-base-900 border border-base-200 flex min-w-20 rounded shadow-sm text-font-default-light dark:border-base-700 dark:text-font-default-dark w-full">
16
16
  {% for choice in choices %}
17
17
  <li class="basis-1/3 border-r border-base-200 flex-grow truncate last:border-r-0 dark:border-base-700 {% if choice.selected %}font-semibold text-primary-600 dark:text-primary-500 {% else %}hover:text-base-700 dark:hover:text-base-200{% endif %}">
18
- <a href="{{ choice.query_string|iriencode }}" title="{{ choice.display }}" class="block px-3 py-2 text-center hover:text-primary-600 dark:hover:text-primary-500">
18
+ <a href="{{ choice.query_string|iriencode }}" title="{{ choice.display }}" class="block py-2 text-center hover:text-primary-600 dark:hover:text-primary-500">
19
19
  {{ choice.display }}
20
20
  </a>
21
21
  </li>
@@ -1,4 +1,4 @@
1
- <div class="border flex flex-col flex-grow overflow-hidden p-6 relative rounded shadow-sm dark:border-base-800 {% if class %} {{ class }}{% endif %}">
1
+ <div class="bg-white border flex flex-col flex-grow overflow-hidden p-6 relative rounded shadow-sm dark:bg-base-900 dark:border-base-800 {% if class %} {{ class }}{% endif %}">
2
2
  {% if title %}
3
3
  <h2 class="bg-base-50 border-b font-semibold mb-6 -mt-6 -mx-6 py-4 px-6 text-font-important-light dark:text-font-important-dark dark:border-base-800 dark:bg-white/[.02]">
4
4
  {{ title }}
@@ -1,31 +1,47 @@
1
- {% load unfold %}
1
+ {% load i18n unfold %}
2
2
 
3
- <div class="{% if card_included == 1 %}-m-6{% else %}lg:border lg:rounded lg:shadow-sm{% endif %} overflow-x-auto lg:dark:border-base-800">
4
- <table class="block border-base-200 border-spacing-none border-separate w-full lg:table">
5
- {% if table.headers %}
6
- <thead class="text-base-900 dark:text-base-100">
7
- <tr class="bg-base-50 dark:bg-white/[.02]">
8
- {% for header in table.headers %}
9
- <th class="align-middle font-semibold py-2 text-left text-sm whitespace-nowrap sortable column-description hidden px-3 lg:table-cell {% if card_included == 1 %}first:pl-6 last:pr-6{% endif %}">
10
- {{ header }}
11
- </th>
12
- {% endfor %}
13
- </tr>
14
- </thead>
15
- {% endif %}
3
+ <div class="flex flex-col">
4
+ {% if title %}
5
+ <h3 class="font-semibold mb-1 text-font-important-light text-sm dark:text-font-important-dark">
6
+ {{ title }}
7
+ </h3>
8
+ {% endif %}
9
+
10
+ <div class="{% if card_included == 1 %}-m-6{% else %} bg-white flex flex-col grow lg:border lg:border-base-200 lg:overflow-hidden lg:rounded lg:shadow-sm{% endif %} lg:dark:border-base-800 dark:bg-base-900">
11
+ <div {% if height %}style="max-height: {{ height }}px;" data-simplebar{% endif %}>
12
+ <table class="block border-spacing-none border-separate w-full lg:table">
13
+ {% if table.headers %}
14
+ <thead class="text-base-900 dark:text-base-100 {% if height %}sticky top-0{% endif %}">
15
+ <tr class="bg-base-50 dark:bg-white/[.02]">
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 %}">
18
+ {{ header|capfirst }}
19
+ </th>
20
+ {% endfor %}
21
+ </tr>
22
+ </thead>
23
+ {% endif %}
16
24
 
17
- {% if table.rows %}
18
- <tbody class="block lg:table-row-group">
19
- {% for row in table.rows %}
20
- <tr class="{% if striped == 1 %}{% cycle '' 'bg-base-50 dark:bg-white/[.02]' %}{% endif %} block {% if not card_included == 1 %}border mb-3 rounded shadow-sm{% else %}border-t{% endif %} lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-base-800">
21
- {% for cell in row %}
22
- <td class="px-3 py-2 align-middle flex border-t border-base-200 font-normal gap-4 min-w-0 overflow-hidden text-left before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto first:border-t-0 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-base-800 {% if card_included == 1 %}lg:first:pl-6 lg:last:pr-6{% endif %}" {% if table.headers %}data-label="{{ table.headers|index:forloop.counter0 }}"{% endif %}>
23
- {{ cell }}
24
- </td>
25
+ {% if table.rows %}
26
+ <tbody class="block lg:table-row-group">
27
+ {% for row in table.rows %}
28
+ <tr class="{% if striped == 1 %}{% cycle '' 'bg-base-50 dark:bg-white/[.02]' %}{% endif %} block group {% if forloop.first %}first-row{% endif %} {% if not card_included == 1 %}border mb-3 rounded shadow-sm{% else %}border-b{% endif %} lg:table-row lg:border-none lg:mb-0 lg:shadow-none dark:border-base-800">
29
+ {% for cell in row %}
30
+ <td class="px-3 py-2 align-middle flex border-t border-base-200 font-normal gap-4 min-w-0 overflow-hidden text-left before:flex before:capitalize before:content-[attr(data-label)] before:items-center before:mr-auto first:border-t-0 lg:group-[.first-row]:border-t-0 lg:before:hidden lg:first:border-t lg:py-3 lg:table-cell dark:border-base-800 {% if card_included == 1 %}lg:first:pl-6 lg:last:pr-6{% endif %}" {% if table.headers %}data-label="{{ table.headers|index:forloop.counter0 }}"{% endif %}>
31
+ {{ cell }}
32
+ </td>
33
+ {% endfor %}
34
+ </tr>
25
35
  {% endfor %}
26
- </tr>
27
- {% endfor %}
28
- </tbody>
36
+ </tbody>
37
+ {% endif %}
38
+ </table>
39
+ </div>
40
+
41
+ {% if not table.rows %}
42
+ <p class="bg-white border border-base-200 flex grow items-center justify-center py-2 rounded shadow-sm dark:bg-base-900 lg:border-0 lg:rounded-none lg:shadow-none">
43
+ {% trans "No data" %}
44
+ </p>
29
45
  {% endif %}
30
- </table>
46
+ </div>
31
47
  </div>
@@ -22,7 +22,7 @@
22
22
  {% endif %}
23
23
 
24
24
  {% if cl.has_active_filters %}
25
- <a href="{{ cl.clear_all_filters_qs }}" class="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">
25
+ <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">
26
26
  {% trans "Clear all filters" %}
27
27
  </a>
28
28
  {% endif %}
@@ -4,6 +4,7 @@ from typing import Any, Optional, Union
4
4
  from django import template
5
5
  from django.contrib.admin.helpers import AdminForm, Fieldset
6
6
  from django.contrib.admin.views.main import ChangeList
7
+ from django.db.models import Model
7
8
  from django.db.models.options import Options
8
9
  from django.forms import Field
9
10
  from django.http import HttpRequest
@@ -85,6 +86,11 @@ def tab_list(context: RequestContext, page: str, opts: Optional[Options] = None)
85
86
  )
86
87
 
87
88
 
89
+ @register.simple_tag(name="render_section", takes_context=True)
90
+ def render_section(context: Context, section_class, instance: Model) -> str:
91
+ return section_class(context.request, instance).render()
92
+
93
+
88
94
  @register.simple_tag(name="has_nav_item_active")
89
95
  def has_nav_item_active(items: list) -> bool:
90
96
  for item in items:
@@ -214,14 +220,17 @@ class RenderComponentNode(template.Node):
214
220
 
215
221
  if "component_class" in values:
216
222
  values = ComponentRegistry.create_instance(
217
- values["component_class"], request=context.request
223
+ values["component_class"],
224
+ request=context.request if hasattr(context, "request") else None,
218
225
  ).get_context_data(**values)
219
226
 
220
227
  if self.include_context:
221
228
  values.update(context.flatten())
222
229
 
223
230
  return render_to_string(
224
- self.template_name, request=context.request, context=values
231
+ self.template_name,
232
+ request=context.request if hasattr(context, "request") else None,
233
+ context=values,
225
234
  )
226
235
 
227
236
 
@@ -16,6 +16,7 @@ from django.contrib.admin.views.main import (
16
16
  )
17
17
  from django.core.exceptions import ObjectDoesNotExist
18
18
  from django.db import models
19
+ from django.db.models import Model
19
20
  from django.forms import Form
20
21
  from django.http import HttpRequest
21
22
  from django.template import Library
@@ -26,13 +27,13 @@ from django.utils.html import format_html
26
27
  from django.utils.safestring import SafeText, mark_safe
27
28
  from django.utils.translation import gettext_lazy as _
28
29
 
29
- from ..utils import (
30
+ from unfold.utils import (
30
31
  display_for_field,
31
32
  display_for_header,
32
33
  display_for_label,
33
34
  display_for_value,
34
35
  )
35
- from ..widgets import UnfoldBooleanWidget
36
+ from unfold.widgets import UnfoldBooleanWidget
36
37
 
37
38
  register = Library()
38
39
 
@@ -336,23 +337,22 @@ def items_for_result(cl: ChangeList, result: HttpRequest, form) -> SafeText:
336
337
 
337
338
  class UnfoldResultList(ResultList):
338
339
  def __init__(
339
- self, instance_pk: Union[int, str], form: Optional[Form], *items: Any
340
+ self,
341
+ instance: Model,
342
+ form: Optional[Form],
343
+ *items: Any,
340
344
  ) -> None:
341
- self.instance_pk = instance_pk
345
+ self.instance = instance
342
346
  super().__init__(form, *items)
343
347
 
344
348
 
345
349
  def results(cl: ChangeList):
346
350
  if cl.formset:
347
351
  for res, form in zip(cl.result_list, cl.formset.forms):
348
- pk = cl.lookup_opts.pk.attname
349
- pk_value = getattr(res, pk)
350
- yield UnfoldResultList(pk_value, form, items_for_result(cl, res, form))
352
+ yield UnfoldResultList(res, form, items_for_result(cl, res, form))
351
353
  else:
352
354
  for res in cl.result_list:
353
- pk = cl.lookup_opts.pk.attname
354
- pk_value = getattr(res, pk)
355
- yield UnfoldResultList(pk_value, None, items_for_result(cl, res, None))
355
+ yield UnfoldResultList(res, None, items_for_result(cl, res, None))
356
356
 
357
357
 
358
358
  def result_list(context: dict[str, Any], cl: ChangeList) -> dict[str, Any]: