django-unfold 0.65.0__py3-none-any.whl → 0.67.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.
Files changed (33) hide show
  1. {django_unfold-0.65.0.dist-info → django_unfold-0.67.0.dist-info}/METADATA +4 -2
  2. {django_unfold-0.65.0.dist-info → django_unfold-0.67.0.dist-info}/RECORD +33 -31
  3. {django_unfold-0.65.0.dist-info → django_unfold-0.67.0.dist-info}/WHEEL +1 -1
  4. unfold/admin.py +1 -1
  5. unfold/contrib/constance/settings.py +1 -0
  6. unfold/contrib/filters/admin/numeric_filters.py +2 -0
  7. unfold/contrib/filters/forms.py +25 -4
  8. unfold/contrib/filters/static/unfold/filters/js/admin-numeric-filter.js +62 -28
  9. unfold/contrib/filters/templates/unfold/filters/filters_numeric_slider.html +2 -11
  10. unfold/contrib/location_field/templates/location_field/map_widget.html +1 -1
  11. unfold/fields.py +41 -0
  12. unfold/sites.py +43 -13
  13. unfold/static/unfold/css/styles.css +1 -1
  14. unfold/static/unfold/js/app.js +117 -14
  15. unfold/static/unfold/js/select2.init.js +2 -9
  16. unfold/styles.css +25 -15
  17. unfold/templates/admin/nav_sidebar.html +1 -1
  18. unfold/templates/unfold/components/button.html +1 -1
  19. unfold/templates/unfold/components/card.html +15 -6
  20. unfold/templates/unfold/components/layer.html +1 -0
  21. unfold/templates/unfold/components/progress.html +14 -8
  22. unfold/templates/unfold/components/tracker.html +1 -1
  23. unfold/templates/unfold/helpers/app_list.html +1 -1
  24. unfold/templates/unfold/helpers/boolean.html +1 -1
  25. unfold/templates/unfold/helpers/command.html +1 -1
  26. unfold/templates/unfold/helpers/command_results.html +31 -12
  27. unfold/templates/unfold/helpers/field_readonly_value.html +1 -1
  28. unfold/templates/unfold/helpers/field_readonly_value_file.html +18 -0
  29. unfold/templates/unfold/helpers/search.html +1 -0
  30. unfold/templates/unfold/widgets/clearable_file_input_small.html +1 -1
  31. unfold/templatetags/unfold.py +44 -7
  32. unfold/widgets.py +3 -3
  33. {django_unfold-0.65.0.dist-info → django_unfold-0.67.0.dist-info/licenses}/LICENSE.md +0 -0
@@ -68,7 +68,7 @@ function searchDropdown() {
68
68
  }
69
69
  },
70
70
  prevItem() {
71
- if (this.currentIndex > 0) {
71
+ if (this.currentIndex > 1) {
72
72
  this.currentIndex--;
73
73
  }
74
74
  },
@@ -93,6 +93,7 @@ function searchCommand() {
93
93
  hasResults: false,
94
94
  openCommandResults: false,
95
95
  currentIndex: 0,
96
+ totalItems: 0,
96
97
  commandHistory: JSON.parse(localStorage.getItem("commandHistory") || "[]"),
97
98
  handleOpen() {
98
99
  this.openCommandResults = true;
@@ -102,6 +103,7 @@ function searchCommand() {
102
103
  }, 20);
103
104
 
104
105
  this.items = document.querySelectorAll("#command-history li");
106
+ this.totalItems = this.items.length;
105
107
  },
106
108
  handleShortcut(event) {
107
109
  if (
@@ -121,21 +123,46 @@ function searchCommand() {
121
123
  this.openCommandResults = false;
122
124
  this.el.innerHTML = "";
123
125
  this.items = undefined;
126
+ this.totalItems = 0;
124
127
  this.currentIndex = 0;
125
128
  } else {
126
129
  this.$refs.searchInputCommand.value = "";
127
130
  }
128
131
  },
129
132
  handleContentLoaded(event) {
130
- this.items = event.target.querySelectorAll("li");
131
- this.currentIndex = 0;
132
- this.hasResults = this.items.length > 0;
133
+ if (
134
+ event.target.id !== "command-results" &&
135
+ event.target.id !== "command-results-list"
136
+ ) {
137
+ return;
138
+ }
139
+
140
+ const commandResultsList = document.getElementById(
141
+ "command-results-list"
142
+ );
143
+ if (commandResultsList) {
144
+ this.items = commandResultsList.querySelectorAll("li");
145
+ this.totalItems = this.items.length;
146
+ } else {
147
+ this.items = undefined;
148
+ this.totalItems = 0;
149
+ }
150
+
151
+ if (event.target.id === "command-results") {
152
+ this.currentIndex = 0;
153
+
154
+ if (this.items) {
155
+ this.totalItems = this.items.length;
156
+ } else {
157
+ this.totalItems = 0;
158
+ }
159
+ }
160
+
161
+ this.hasResults = this.totalItems > 0;
133
162
 
134
163
  if (!this.hasResults) {
135
164
  this.items = document.querySelectorAll("#command-history li");
136
165
  }
137
-
138
- new SimpleBar(event.target);
139
166
  },
140
167
  handleOutsideClick() {
141
168
  this.$refs.searchInputCommand.value = "";
@@ -158,7 +185,7 @@ function searchCommand() {
158
185
  }
159
186
  },
160
187
  nextItem() {
161
- if (this.currentIndex < this.items.length) {
188
+ if (this.currentIndex < this.totalItems) {
162
189
  this.currentIndex++;
163
190
  this.scrollToActiveItem();
164
191
  }
@@ -389,6 +416,12 @@ const DEFAULT_CHART_OPTIONS = {
389
416
  pointBorderWidth: 0,
390
417
  pointStyle: false,
391
418
  },
419
+ pie: {
420
+ borderWidth: 0,
421
+ },
422
+ doughnut: {
423
+ borderWidth: 0,
424
+ },
392
425
  },
393
426
  plugins: {
394
427
  legend: {
@@ -409,6 +442,13 @@ const DEFAULT_CHART_OPTIONS = {
409
442
  },
410
443
  scales: {
411
444
  x: {
445
+ display: function (context) {
446
+ if (["pie", "doughnut", "radar"].includes(context.chart.config.type)) {
447
+ return false;
448
+ }
449
+
450
+ return true;
451
+ },
412
452
  border: {
413
453
  dash: [5, 5],
414
454
  dashOffset: 2,
@@ -417,6 +457,11 @@ const DEFAULT_CHART_OPTIONS = {
417
457
  ticks: {
418
458
  color: "#9ca3af",
419
459
  display: true,
460
+ maxTicksLimit: function (context) {
461
+ return context.chart.data.datasets.find(
462
+ (dataset) => dataset.maxTicksXLimit
463
+ )?.maxTicksXLimit;
464
+ },
420
465
  },
421
466
  grid: {
422
467
  display: true,
@@ -424,15 +469,35 @@ const DEFAULT_CHART_OPTIONS = {
424
469
  },
425
470
  },
426
471
  y: {
472
+ display: function (context) {
473
+ if (["pie", "doughnut", "radar"].includes(context.chart.config.type)) {
474
+ return false;
475
+ }
476
+
477
+ return true;
478
+ },
427
479
  border: {
428
480
  dash: [5, 5],
429
481
  dashOffset: 5,
430
482
  width: 0,
431
483
  },
432
484
  ticks: {
433
- display: false,
434
- font: {
435
- size: 13,
485
+ color: "#9ca3af",
486
+ display: function (context) {
487
+ return context.chart.data.datasets.some((dataset) => {
488
+ return (
489
+ dataset.hasOwnProperty("displayYAxis") && dataset.displayYAxis
490
+ );
491
+ });
492
+ },
493
+ callback: function (value) {
494
+ const suffix = this.chart.data.datasets.find(
495
+ (dataset) => dataset.suffixYAxis
496
+ )?.suffixYAxis;
497
+ if (suffix) {
498
+ return `${value} ${suffix}`;
499
+ }
500
+ return value;
436
501
  },
437
502
  },
438
503
  grid: {
@@ -467,8 +532,17 @@ const renderCharts = () => {
467
532
  const borderColor = hasDarkClass ? baseColorDark : baseColorLight;
468
533
 
469
534
  for (const chart of charts) {
470
- chart.options.scales.x.grid.color = borderColor;
471
- chart.options.scales.y.grid.color = borderColor;
535
+ if (chart.options.scales.x) {
536
+ chart.options.scales.x.grid.color = borderColor;
537
+ }
538
+
539
+ if (chart.options.scales.y) {
540
+ chart.options.scales.y.grid.color = borderColor;
541
+ }
542
+
543
+ if (chart.options.scales.r) {
544
+ chart.options.scales.r.grid.color = borderColor;
545
+ }
472
546
  chart.update();
473
547
  }
474
548
  };
@@ -488,7 +562,17 @@ const renderCharts = () => {
488
562
  for (const key in parsedData.datasets) {
489
563
  const dataset = parsedData.datasets[key];
490
564
  const processColor = (colorProp) => {
491
- if (dataset?.[colorProp]?.startsWith("var(")) {
565
+ if (Array.isArray(dataset?.[colorProp])) {
566
+ for (const [index, prop] of dataset?.[colorProp].entries()) {
567
+ if (prop.startsWith("var(")) {
568
+ const cssVar = prop.match(/var\((.*?)\)/)[1];
569
+ const color = getComputedStyle(document.documentElement)
570
+ .getPropertyValue(cssVar)
571
+ .trim();
572
+ dataset[colorProp][index] = color;
573
+ }
574
+ }
575
+ } else if (dataset?.[colorProp]?.startsWith("var(")) {
492
576
  const cssVar = dataset[colorProp].match(/var\((.*?)\)/)[1];
493
577
  const color = getComputedStyle(document.documentElement)
494
578
  .getPropertyValue(cssVar)
@@ -501,11 +585,30 @@ const renderCharts = () => {
501
585
  processColor("backgroundColor");
502
586
  }
503
587
 
588
+ CHART_OPTIONS = { ...DEFAULT_CHART_OPTIONS };
589
+ if (type === "radar") {
590
+ CHART_OPTIONS.scales = {
591
+ r: {
592
+ ticks: {
593
+ backdropColor: "transparent",
594
+ },
595
+ pointLabels: {
596
+ color: "#9ca3af",
597
+ font: {
598
+ size: 12,
599
+ },
600
+ },
601
+ },
602
+ };
603
+ }
604
+ Chart.defaults.font.family = "Inter";
605
+ Chart.defaults.font.size = 12;
606
+
504
607
  charts.push(
505
608
  new Chart(ctx, {
506
609
  type: type || "bar",
507
610
  data: parsedData,
508
- options: options ? JSON.parse(options) : DEFAULT_CHART_OPTIONS,
611
+ options: options ? JSON.parse(options) : { ...CHART_OPTIONS },
509
612
  })
510
613
  );
511
614
  }
@@ -18,7 +18,7 @@
18
18
  return this;
19
19
  };
20
20
 
21
- $.fn.djangoAdminSelect2 = function () {
21
+ $.fn.djangoFilterSelect2 = function () {
22
22
  $.each(this, function (i, element) {
23
23
  $(element).select2({
24
24
  ajax: {
@@ -40,13 +40,6 @@
40
40
  $(function () {
41
41
  $(".unfold-admin-autocomplete.admin-autocomplete").djangoCustomSelect2();
42
42
 
43
- $(".admin-autocomplete")
44
- .not(".unfold-admin-autocomplete")
45
- .not("[name*=__prefix__]")
46
- .djangoAdminSelect2();
47
- });
48
-
49
- document.addEventListener("formset:added", (event) => {
50
- $(event.target).find(".admin-autocomplete").djangoAdminSelect2();
43
+ $(".unfold-filter-autocomplete.admin-autocomplete").djangoFilterSelect2();
51
44
  });
52
45
  }
unfold/styles.css CHANGED
@@ -9,19 +9,20 @@
9
9
  }
10
10
  }
11
11
 
12
- @source inline("bg-white/[.06] border-white/40 h-3 w-3 h-[64px] w-[65px]! left-[65px] {dark:,}text-base-{50,{100..900},950} dark:hover:bg-white/[.06] translate-x-1/4 -translate-y-1/4");
13
- @source inline("{-,}{top,bottom,left,right}-{0..6}");
12
+ @source inline("aspect-square bg-white/[.06] border-white/40 h-3 w-3 h-[64px] w-[65px]! left-[65px] {dark:,}text-base-{50,{100..900},950} dark:hover:bg-white/[.06] {-,}translate-{x,y}-{1/2}");
13
+ @source inline("{-,}{top,bottom,left,right}-{1/2,0..6} {!,}leading-[38px] text-{default,subtle,important}");
14
+ @source inline("bg-linear-to-{{t,r,b,l,tl,tr,bl,br}} {dark:,}{from, to}-{{{base,primary}-{50,{100..900..100},950}},white}");
14
15
  @source inline("rounded rounded-{t,r,b,l} rounded-{t,r,b,l}-default");
15
16
  @source inline("{md:,lg:,2xl:,}relative {md:,lg:,2xl:,}flex {md:,lg:,2xl:,}absolute {md:,lg:,2xl:,}sticky");
16
- @source inline("{-,}m{t,r,b,l,x,y,}-{0..6} {-,}p{t,r,b,l,x,y,}-{0..6}");
17
+ @source inline("{-,}{lg:,}m{t,r,b,l,x,y,}-{0..24} {-,}{lg:,}p{t,r,b,l,x,y,}-{0..24}");
17
18
  @source inline("{md:,lg:,}border-{0,2} {md:,lg:,}border-{t,r,b,l}-{0,2}");
18
- @source inline("{hover:,dark:,}bg-primary-{50,{100..900..100},950}");
19
+ @source inline("{!,}{hover:,dark:,}bg-primary-{50,{100..900..100},950}");
19
20
  @source inline("{hover:,dark:,}border-base-{50,{100..900..100},950} {hover:,dark:,}border-transparent");
20
21
  @source inline("{hover:,dark:,}bg-base-{50,{100..900..100},950}");
21
- @source inline("{md:,lg:,}w-{1/2,1/3,2/3,1/4,2/4,3/4,1/5,2/5,3/5,4/5,1/6,2/6,3/6,4/6,5/6,1/7,2/7,3/7,4/7,5/7,6/7}");
22
- @source inline("{md:,lg:,}gap-{0.5,{1..12}}");
23
- @source inline("{md:,lg:,}grid-cols-{1..12}");
24
- @source inline("{md:,lg:,}col-span-{1..12}");
22
+ @source inline("{md:,lg:,}{min-h,h,min-w,w}-{1..8} {md:,lg:,}w-{1/2,1/3,2/3,1/4,2/4,3/4,1/5,2/5,3/5,4/5,1/6,2/6,3/6,4/6,5/6,1/7,2/7,3/7,4/7,5/7,6/7}");
23
+ @source inline("{md:,lg:,xl:,}gap-{0.5,{1..16}}");
24
+ @source inline("{md:,lg:,xl:,}grid-cols-{1..16}");
25
+ @source inline("{md:,lg:,xl:,}col-span-{1..16}");
25
26
 
26
27
  @layer base {
27
28
  *,
@@ -29,7 +30,7 @@
29
30
  ::before,
30
31
  ::backdrop,
31
32
  ::file-selector-button {
32
- border-color: var(--color-gray-200, currentColor);
33
+ border-color: var(--color-base-200, currentColor);
33
34
  }
34
35
  }
35
36
 
@@ -74,6 +75,19 @@
74
75
  --color-font-important-dark: var(--color-font-important-dark);
75
76
  }
76
77
 
78
+
79
+ @utility text-important {
80
+ @apply text-font-important-light dark:text-font-important-dark;
81
+ }
82
+
83
+ @utility text-default {
84
+ @apply text-font-default-light dark:text-font-default-dark;
85
+ }
86
+
87
+ @utility text-subtle {
88
+ @apply text-font-subtle-light dark:text-font-subtle-dark;
89
+ }
90
+
77
91
  @utility field-sizing-content {
78
92
  field-sizing: content;
79
93
  }
@@ -99,7 +113,7 @@
99
113
  }
100
114
 
101
115
  @utility scrollable-top {
102
- @apply after:absolute after:content-[''] after:bg-linear-to-t after:from-gray-100 after:h-4 after:left-0 after:right-0 after:-top-px after:-translate-y-full dark:after:bg-none;
116
+ @apply after:absolute after:content-[''] after:bg-linear-to-t after:from-base-100 after:h-4 after:left-0 after:right-0 after:-top-px after:-translate-y-full dark:after:bg-none;
103
117
  }
104
118
 
105
119
  @utility md-16 {
@@ -264,7 +278,7 @@ table tr.selected th {
264
278
 
265
279
  .selector-chooseall,
266
280
  .selector-clearall {
267
- @apply block border-t border-base-200 py-2 text-center text-sm text-primary-500 dark:border-base-700;
281
+ @apply block border-t border-base-200 cursor-pointer py-2 text-center text-sm text-default;
268
282
  }
269
283
 
270
284
  .selector-clearall {
@@ -623,10 +637,6 @@ fieldset details[open] > summary:after {
623
637
  @apply bg-primary-600 border-0 h-1;
624
638
  }
625
639
 
626
- #changelist-filter .admin-numeric-filter-slider-tooltips {
627
- @apply flex flex-row font-medium mb-5 space-x-4 text-base-500 text-sm;
628
- }
629
-
630
640
  /*******************************************************
631
641
  Trix
632
642
  *******************************************************/
@@ -2,7 +2,7 @@
2
2
 
3
3
  <div class="relative z-50">
4
4
  <div class="fixed hidden xl:relative xl:block w-[288px] {% element_classes 'navigation_wrapper' %}" x-bind:class="{'xl:hidden!': !sidebarDesktopOpen, 'block!': sidebarMobileOpen}">
5
- <div class="bg-gray-50 relative z-30 dark:bg-gray-900">
5
+ <div class="bg-base-50 relative z-30 dark:bg-base-900">
6
6
  <div id="nav-sidebar" class="bg-base-50 border-r border-base-200 bottom-0 fixed max-h-screen top-0 w-[288px] dark:border-base-800 dark:bg-base-900 {% element_classes 'navigation' %}">
7
7
  {% if templates.navigation %}
8
8
  {% include templates.navigation %}
@@ -3,7 +3,7 @@ class="font-medium flex group items-center gap-2 px-3 py-2 rounded-default justi
3
3
  {% if variant == "default" %}
4
4
  border border-base-200 bg-white text-font-important-light dark:border-base-700 dark:bg-transparent dark:text-font-important-dark
5
5
  {% elif variant == "secondary" %}
6
- bg-gray-100 border border-transparent dark:bg-base-800
6
+ bg-base-100 border border-transparent dark:bg-base-800
7
7
  {% elif variant == "ghost" %}
8
8
  bg-transparent text-font-important-light dark:text-font-important-dark
9
9
  {% else %}
@@ -1,11 +1,20 @@
1
- <{% if href %}a href="{{ href }}"{% else %}div{% endif %} class="bg-white block border border-base-200 flex flex-col grow overflow-hidden p-6 relative rounded-default shadow-xs dark:bg-base-900 dark:border-base-800 {% if href %}cursor-pointer transition-all hover:border-base-300 hover:shadow-md hover:shadow-base-200 dark:hover:border-base-700 dark:hover:shadow-base-800/50 {% endif %}{% if class %} {{ class }}{% endif %}">
1
+ <{% if href %}a href="{{ href }}"{% else %}div{% endif %} class="bg-white block flex flex-col grow overflow-hidden p-6 relative rounded-default shadow-xs dark:bg-base-900 dark:border-base-800 {% if not disable_border %}border border-base-200 {% else %}shadow-base-500/20{% endif %} {% if href %}cursor-pointer transition-all hover:border-base-300 hover:shadow-md dark:hover:border-base-700 dark:hover:shadow-base-800/50 {% endif %}{% if class %} {{ class }}{% endif %}">
2
+
2
3
  {% if title %}
3
- <h2 class="border-b border-base-200 font-semibold mb-6 -mt-6 -mx-6 py-4 px-6 text-[15px] text-font-important-light dark:text-font-important-dark dark:border-base-800">
4
- {{ title }}
5
- </h2>
4
+ <div class="border-b border-base-200 flex flex-row items-center mb-6 -mt-6 -mx-6 py-4 px-6 dark:border-base-800">
5
+ <h2 class="font-semibold text-[15px] text-important">
6
+ {{ title }}
7
+ </h2>
8
+
9
+ {% if action %}
10
+ <div class="ml-auto">
11
+ {{ action }}
12
+ </div>
13
+ {% endif %}
14
+ </div>
6
15
  {% endif %}
7
16
 
8
- <div class="grow relative {% if icon %} pl-6{% endif %}">
17
+ <div class="grow relative{% if icon %} pl-8{% endif %}">
9
18
  {{ children }}
10
19
 
11
20
  {% if label %}
@@ -15,7 +24,7 @@
15
24
  {% endif %}
16
25
 
17
26
  {% if icon %}
18
- <span class="material-symbols-outlined absolute -left-6 text-base-300 top-1/2 -translate-x-1/3 -translate-y-1/2 text-6xl! dark:text-base-500">{{ icon }}</span>
27
+ <span class="material-symbols-outlined absolute -left-6 text-base-300 top-1/2 -translate-x-1/3 -translate-y-1/2 text-6xl! dark:text-base-500 {{ icon_class }}">{{ icon }}</span>
19
28
  {% endif %}
20
29
  </div>
21
30
 
@@ -0,0 +1 @@
1
+ {{ children }}
@@ -1,5 +1,5 @@
1
- <div class="{% if class %}{{ class }}{% endif %}">
2
- <div class="flex flex-col gap-2 relative">
1
+ <div class="flex flex-col gap-2 relative w-full {% if class %}{{ class }}{% endif %}">
2
+ {% if title or description %}
3
3
  <div class="flex flex-row relative z-20">
4
4
  {% if title %}
5
5
  <h3 class="text-font-important-light dark:text-font-important-dark text-sm">
@@ -13,11 +13,17 @@
13
13
  </strong>
14
14
  {% endif %}
15
15
  </div>
16
+ {% endif %}
16
17
 
17
- {% if value %}
18
- <div class="bg-gray-100 overflow-hidden rounded-default dark:bg-base-800">
19
- <div class="h-1.5 bg-primary-600 rounded-default z-10 dark:bg-primary-500" title="{{ value }}%" style="width: {{ value }}%"></div>
20
- </div>
21
- {% endif %}
22
- </div>
18
+ {% if items or value %}
19
+ <div class="bg-base-100 flex flex-row overflow-hidden rounded-default dark:bg-base-800">
20
+ {% if items %}
21
+ {% for item in items %}
22
+ <div class="h-1.5 bg-primary-600 z-10 dark:bg-primary-500 last:rounded-r-default {{ item.progress_class }}" title="{% if item.title %}{{ item.title }}: {% endif %}{{ item.value }}%" style="width: {{ item.value }}%"></div>
23
+ {% endfor %}
24
+ {% elif value %}
25
+ <div class="h-1.5 bg-primary-600 rounded-default z-10 last:rounded-r-default dark:bg-primary-500 {{ progress_class }}" title="{% if title %}{{ title }}: {% endif %}{{ value }}%" style="width: {{ value }}%"></div>
26
+ {% endif %}
27
+ </div>
28
+ {% endif %}
23
29
  </div>
@@ -1,4 +1,4 @@
1
- <ul class="flex flex-row gap-0.5 overflow-hidden rounded-default">
1
+ <ul class="flex flex-row gap-0.5 overflow-hidden rounded-default {% if class %}{{ class }}{% endif %}">
2
2
  {% for item in data %}
3
3
  <li class="h-8 px-px size-full {% if item.color %}{{ item.color }}{% else %}bg-base-300 dark:bg-base-400{% endif %} hover:opacity-50" title="{{ item.tooltip }}"></li>
4
4
  {% endfor %}
@@ -72,7 +72,7 @@
72
72
  <div class="absolute bottom-0 left-0 right-0 top-0 z-50 md:left-72" x-cloak x-show="openAllApplications">
73
73
  <div class="absolute bg-base-900/80 backdrop-blur-xs bottom-0 left-0 right-0 top-0 z-10 w-screen"></div>
74
74
 
75
- <div class="bg-white flex flex-col h-full overflow-x-hidden overflow-y-auto py-5 px-8 relative text-sm w-80 z-20 dark:bg-base-900 dark:border-r dark:border-base-800" x-on:click.outside="openAllApplications = false" x-on:keydown.escape.window="openAllApplications = false">
75
+ <div class="bg-white flex flex-col h-full overflow-x-hidden overflow-y-auto py-5 px-8 relative text-sm w-80 z-20 dark:bg-base-900 dark:border-r dark:border-base-800" x-on:click.outside="openAllApplications = false" x-on:keydown.escape.window="openAllApplications = false" data-simplebar>
76
76
  {% for app in app_list %}
77
77
  <div class="mb-6 last:mb-0">
78
78
  <h2 class="mb-4 font-semibold text-font-important-light truncate dark:text-font-important-dark">
@@ -1,6 +1,6 @@
1
1
  {% load i18n %}
2
2
 
3
- <div class="inline-flex font-semibold items-center justify-center leading-normal h-6 w-6 rounded-full uppercase whitespace-nowrap {% if value == '' or value == None %}bg-base-100 text-base-700 dark:bg-base-500/20 dark:text-base-200{% elif value %}bg-green-100 text-green-700 dark:bg-green-500/20 dark:text-green-400{% else %}bg-red-100 text-red-700 dark:bg-red-500/20 dark:text-red-400{% endif %}"
3
+ <div class="inline-flex font-semibold items-center justify-center leading-normal h-6 w-6 !rounded-full uppercase whitespace-nowrap {% if value == '' or value == None %}bg-base-100 text-base-700 dark:bg-base-500/20 dark:text-base-200{% elif value %}bg-green-100 text-green-700 dark:bg-green-500/20 dark:text-green-400{% else %}bg-red-100 text-red-700 dark:bg-red-500/20 dark:text-red-400{% endif %}"
4
4
  title="{% if value == '' or value == None %}{% trans "Unknown" %}{% elif value %}{% trans "True" %}{% else %}{% trans "False" %}{% endif %}">
5
5
 
6
6
  <span class="material-symbols-outlined">
@@ -30,7 +30,7 @@
30
30
  x-on:keydown.escape.prevent="handleEscape()"
31
31
  x-on:keydown.arrow-down.prevent="nextItem()"
32
32
  x-on:keydown.arrow-up.prevent="prevItem()"
33
- x-on:keydown.enter.prevent="selectItem()"
33
+ x-on:keydown.enter.prevent="selectItem({% if command_show_history %}true{% else %}false{% endif %})"
34
34
  hx-get="{% url "admin:search" %}?extended=1"
35
35
  hx-trigger="keyup changed delay:500ms"
36
36
  hx-select="#command-results-list"
@@ -1,12 +1,20 @@
1
- {% load i18n %}
1
+ {% load i18n unfold %}
2
2
 
3
3
  {% if results %}
4
4
  <ul id="command-results-list" class="flex flex-col gap-1.5 p-4">
5
5
  {% for item in results %}
6
6
  <li class="group"
7
- x-bind:class="{'active': currentIndex === {{ forloop.counter }}}"
8
- x-on:mouseenter="currentIndex = {{ forloop.counter }}">
9
- <a class="bg-base-100 flex items-center rounded-default px-3.5 py-3 transition-colors group-[.active]:bg-primary-600 group-[.active]:text-white dark:bg-white/[.04] dark:text-base-200 dark:group-[.active]:bg-primary-600 dark:group-[.active]:text-white"
7
+ {% if forloop.last and results.next_page_number %}
8
+ hx-get="{% url "admin:search" %}{% unfold_querystring extended=1 page=results.next_page_number %}"
9
+ hx-trigger="intersect once threshold:0.5"
10
+ hx-swap="beforeend"
11
+ hx-select="#command-results-list > *"
12
+ hx-target="#command-results-list"
13
+ hx-indicator="#command-results-loading"
14
+ {% endif %}
15
+ x-bind:class="{'active': currentIndex === {{ page_counter|add:forloop.counter }}}"
16
+ x-on:mouseenter="currentIndex = {{ page_counter|add:forloop.counter }}">
17
+ <a class="bg-base-100 flex items-center rounded-default px-3.5 py-3 group-[.active]:bg-primary-600 group-[.active]:text-white dark:bg-white/[.04] dark:text-base-200 dark:group-[.active]:bg-primary-600 dark:group-[.active]:text-white"
10
18
  href="{{ item.link }}"
11
19
  data-title="{{ item.title }}"
12
20
  data-description="{{ item.description }}"
@@ -25,26 +33,37 @@
25
33
  <span class="text-font-subtle-light dark:text-font-subtle-dark group-[.active]:text-white">{{ item.description|capfirst }}</span>
26
34
  </span>
27
35
 
28
- <span class="material-symbols-outlined ml-auto text-sm transition-all group-[.active]:text-white group-[.active]:-mr-1">arrow_forward</span>
36
+ <span class="material-symbols-outlined ml-auto text-sm transition-transform group-[.active]:text-white group-[.active]:-translate-x-[2px]">arrow_forward</span>
29
37
  </a>
30
38
  </li>
31
39
  {% endfor %}
32
40
  </ul>
33
41
  {% else %}
34
- <ul id="command-results-list" class="px-4 py-8 flex items-center justify-center">
42
+ <ul class="px-4 py-8 flex items-center justify-center">
35
43
  <li class="text-lg">
36
44
  {% trans "No results matching your query" %}
37
45
  </li>
38
46
  </ul>
39
47
  {% endif %}
40
48
 
41
-
42
49
  <div id="command-results-note" x-show="hasResults">
43
50
  <div class="border-t border-base-200 px-4 py-3 flex items-center justify-center text-xs dark:border-base-700">
44
- {% blocktranslate count counter=results|length with time=execution_time|floatformat:2 %}
45
- Found {{ counter }} result in {{ time }} seconds
46
- {% plural %}
47
- Found {{ counter }} results in {{ time }} seconds
48
- {% endblocktranslate %}
51
+ <div id="command-results-loading" class="hidden flex-row gap-2 grow h-[16px] items-center justify-center w-[16px] w-full [&.htmx-request]:flex [&.htmx-request+div]:hidden">
52
+ <span class="material-symbols-outlined animate-spin text-sm">
53
+ progress_activity
54
+ </span>
55
+
56
+ <span>
57
+ {% trans "Loading more results..." %}
58
+ </span>
59
+ </div>
60
+
61
+ <div>
62
+ {% blocktranslate count counter=page_obj.count with time=execution_time|floatformat:2 %}
63
+ Found {{ counter }} result in {{ time }} seconds
64
+ {% plural %}
65
+ Found {{ counter }} results in {{ time }} seconds
66
+ {% endblocktranslate %}
67
+ </div>
49
68
  </div>
50
69
  </div>
@@ -1 +1 @@
1
- <div class="readonly break-words {% if field.is_json %}max-w-4xl{% else %}max-w-2xl{% endif %} py-2 text-sm *:rounded-default {% if not adminform.model_admin.compressed_fields and not field.is_image %}bg-base-50 border border-base-200 font-medium px-3 rounded-default shadow-xs dark:border-base-700 dark:bg-base-800{% endif %} {% if field.is_image %}inline-block [&_img]:rounded-default py-0!{% endif %}">{% if value %}{{ value }}{% elif field.contents %}{{ field.contents }}{% else %}-{% endif %}</div>
1
+ <div class="readonly break-words {% if field.is_json %}max-w-4xl{% else %}max-w-2xl{% endif %} py-2 text-sm *:rounded-default {% if not adminform.model_admin.compressed_fields and not field.is_image %}bg-base-50 border border-base-200 font-medium px-3 rounded-default shadow-xs dark:border-base-700 dark:bg-base-800{% endif %}">{% if value %}{{ value }}{% elif field.contents %}{% if field.is_file %}{% include "unfold/helpers/field_readonly_value_file.html" %}{% else %}{{ field.contents }}{% endif %}{% else %}-{% endif %}</div>
@@ -0,0 +1,18 @@
1
+ {% load i18n %}
2
+
3
+ {% with url=field.url %}
4
+ {% if not url %}
5
+ <span class="flex items-center">-<span>
6
+ {% elif field.is_image %}
7
+ <a href="{{ url }}" target="_blank" class="block max-w-48">
8
+ <img src="{{ url }}" alt="{% trans 'Image preview' %}" class="block rounded-default" />
9
+ </a>
10
+ {% else %}
11
+ <a href="{{ url }}" target="_blank">
12
+ <span class="flex items-center gap-2">
13
+ <span class="text-primary-600 dark:text-primary-500">{% trans "Click to download" %}</span>
14
+ <span class="material-symbols-outlined text-base-400 dark:text-base-500">download</span>
15
+ </span>
16
+ </a>
17
+ {% endif %}
18
+ {% endwith %}
@@ -27,6 +27,7 @@
27
27
  name="s"
28
28
  x-ref="searchInput"
29
29
  x-on:focus="openSearchResults = true; currentIndex = 0"
30
+ x-on:keydown="openSearchResults = true;"
30
31
  x-on:keydown.arrow-down.prevent="nextItem()"
31
32
  x-on:keydown.arrow-up.prevent="prevItem()"
32
33
  x-on:keydown.escape.prevent="openSearchResults = false; if ($refs.searchInput.value === '') { $refs.searchInput.blur() } else { $refs.searchInput.value = '' }"
@@ -3,7 +3,7 @@
3
3
  <div class="flex flex-row">
4
4
  <div class="{{ widget.file_wrapper_class }}">
5
5
  {% if widget.is_initial and not widget.required %}
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">
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-base-700">
7
7
  <label for="{{ widget.checkbox_id }}" class="flex items-center">
8
8
  <input type="checkbox"{% if widget.class %} class="{{ widget.class }}"{% endif %} name="{{ widget.checkbox_name }}" id="{{ widget.checkbox_id }}" />
9
9