django-smartbase-admin 0.2.87__py3-none-any.whl → 0.2.89__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_smartbase_admin/admin/admin_base.py +0 -1
- django_smartbase_admin/engine/admin_base_view.py +1 -0
- django_smartbase_admin/engine/filter_widgets.py +18 -13
- django_smartbase_admin/static/sb_admin/dist/chart.js +1 -1
- django_smartbase_admin/static/sb_admin/dist/main.js +1 -1
- django_smartbase_admin/static/sb_admin/dist/main_style.css +1 -1
- django_smartbase_admin/static/sb_admin/dist/table.js +1 -1
- django_smartbase_admin/static/sb_admin/src/css/_components.css +26 -0
- django_smartbase_admin/static/sb_admin/src/css/_datepicker.css +3 -0
- django_smartbase_admin/static/sb_admin/src/css/_inlines.css +1 -0
- django_smartbase_admin/static/sb_admin/src/css/components/_query-builder.css +2 -1
- django_smartbase_admin/static/sb_admin/src/js/datepicker.js +71 -9
- django_smartbase_admin/static/sb_admin/src/js/datepicker_plugins.js +72 -46
- django_smartbase_admin/static/sb_admin/src/js/table_modules/advanced_filter_module.js +16 -9
- django_smartbase_admin/static/sb_admin/src/js/utils.js +5 -0
- django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/date_field.html +9 -2
- django_smartbase_admin/templates/sb_admin/filter_widgets/date_field.html +15 -5
- django_smartbase_admin/templates/sb_admin/includes/inline_fieldset.html +1 -1
- django_smartbase_admin/templates/sb_admin/includes/table_inline_delete_button.html +4 -5
- django_smartbase_admin/templates/sb_admin/inlines/table_inline.html +21 -3
- django_smartbase_admin/views/translations_view.py +1 -1
- {django_smartbase_admin-0.2.87.dist-info → django_smartbase_admin-0.2.89.dist-info}/METADATA +1 -1
- {django_smartbase_admin-0.2.87.dist-info → django_smartbase_admin-0.2.89.dist-info}/RECORD +25 -25
- {django_smartbase_admin-0.2.87.dist-info → django_smartbase_admin-0.2.89.dist-info}/LICENSE.md +0 -0
- {django_smartbase_admin-0.2.87.dist-info → django_smartbase_admin-0.2.89.dist-info}/WHEEL +0 -0
|
@@ -467,4 +467,30 @@
|
|
|
467
467
|
.dropdown-menu {
|
|
468
468
|
@apply py-0;
|
|
469
469
|
}
|
|
470
|
+
|
|
471
|
+
.sticky-table-head,
|
|
472
|
+
.sticky-table-col {
|
|
473
|
+
position: sticky !important;
|
|
474
|
+
right: 0;
|
|
475
|
+
z-index: 1;
|
|
476
|
+
@apply overflow-hidden !border-l-0 pl-8;
|
|
477
|
+
&:before {
|
|
478
|
+
content: '';
|
|
479
|
+
box-shadow: 0 0 8px 0 rgba(17, 24, 39, 0.16);
|
|
480
|
+
@apply absolute inset-0 transition-colors ml-8;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.sticky-table-head {
|
|
485
|
+
@apply bg-transparent !important;
|
|
486
|
+
&:before {
|
|
487
|
+
@apply bg-dark-50;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.sticky-table-col {
|
|
492
|
+
&:before {
|
|
493
|
+
@apply bg-light;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
470
496
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import flatpickr from "flatpickr"
|
|
2
2
|
import {createIcon} from "./utils"
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createRadioInput,
|
|
5
|
+
customActionsPlugin,
|
|
6
|
+
monthYearViewsPlugin
|
|
7
|
+
} from "./datepicker_plugins"
|
|
4
8
|
|
|
5
9
|
|
|
6
10
|
export default class Datepicker {
|
|
@@ -28,20 +32,38 @@ export default class Datepicker {
|
|
|
28
32
|
if(datePickerEl.dataset.sbadminDatepicker) {
|
|
29
33
|
sbadminDatepickerData = JSON.parse(datePickerEl.dataset.sbadminDatepicker)
|
|
30
34
|
}
|
|
35
|
+
|
|
31
36
|
flatpickr(datePickerEl, {
|
|
32
37
|
onReady: (selectedDates, dateStr, instance) => {
|
|
33
|
-
const isInTable = datePickerEl.closest('[data-filter-input-name]')
|
|
34
|
-
if(!isInTable) {
|
|
35
|
-
this.createClear(instance)
|
|
36
|
-
}
|
|
37
38
|
instance.nextMonthNav?.replaceChildren(createIcon('Right-small'))
|
|
38
39
|
instance.prevMonthNav?.replaceChildren(createIcon('Left-small'))
|
|
39
|
-
|
|
40
|
+
|
|
41
|
+
const isInTable = datePickerEl.closest('[data-filter-input-name]')
|
|
42
|
+
|
|
43
|
+
// real input element should be present only in filters
|
|
44
|
+
const realInput = document.getElementById(datePickerEl.dataset.sbadminDatepickerRealInputId)
|
|
45
|
+
const mainInput = realInput || datePickerEl
|
|
46
|
+
mainInput.addEventListener('clear', () => {
|
|
40
47
|
instance.clear()
|
|
41
48
|
})
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if(!isInTable){
|
|
52
|
+
this.createClear(instance)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if(isInTable && realInput) {
|
|
56
|
+
// set initial value from real input to flatpickr
|
|
57
|
+
realInput.addEventListener('SBTableFilterFormLoad', () => {
|
|
58
|
+
if(!datePickerEl.value) {
|
|
59
|
+
instance.setDate(realInput.value, false, instance.config.dateFormat)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
if(realInput) {
|
|
65
|
+
// advanced filters
|
|
66
|
+
instance.setDate(realInput.value, false, instance.config.dateFormat)
|
|
45
67
|
}
|
|
46
68
|
},
|
|
47
69
|
onClose: function(selectedDates, dateStr, instance) {
|
|
@@ -50,12 +72,52 @@ export default class Datepicker {
|
|
|
50
72
|
instance.setDate([selectedDates[0],selectedDates[0]], true)
|
|
51
73
|
}
|
|
52
74
|
},
|
|
75
|
+
onChange: function(selectedDates, dateStr) {
|
|
76
|
+
const realInput = document.getElementById(datePickerEl.dataset.sbadminDatepickerRealInputId)
|
|
77
|
+
if(realInput) {
|
|
78
|
+
realInput.value = dateStr
|
|
79
|
+
realInput.removeAttribute('data-label')
|
|
80
|
+
realInput.dispatchEvent(new Event('change'))
|
|
81
|
+
}
|
|
82
|
+
},
|
|
53
83
|
...options,
|
|
54
84
|
...sbadminDatepickerData.flatpickrOptions,
|
|
55
85
|
...optionsOverride
|
|
56
86
|
})
|
|
57
87
|
}
|
|
58
88
|
|
|
89
|
+
initShortcutsDropdown(datePickerEl) {
|
|
90
|
+
const realInput = document.getElementById(datePickerEl.dataset.sbadminDatepickerRealInputId)
|
|
91
|
+
const baseId = realInput.id
|
|
92
|
+
const baseValue = realInput.value
|
|
93
|
+
const el = document.createElement('div')
|
|
94
|
+
const shortcuts = JSON.parse(datePickerEl.dataset.sbadminDatepickerShortcuts)
|
|
95
|
+
el.classList.add('flatpickr-shortcuts', 'dropdown-menu')
|
|
96
|
+
el.addEventListener('change', (e) => {
|
|
97
|
+
realInput.value = e.target.value
|
|
98
|
+
datePickerEl.value = e.target.nextElementSibling.innerText
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
shortcuts.forEach((shortcut, idx) => {
|
|
102
|
+
const checked = JSON.stringify(shortcut.value) === baseValue
|
|
103
|
+
if(checked) {
|
|
104
|
+
datePickerEl.value = shortcut.label
|
|
105
|
+
}
|
|
106
|
+
el.append(createRadioInput(
|
|
107
|
+
`${baseId}_range${idx}`,
|
|
108
|
+
`${baseId}_shortcut`,
|
|
109
|
+
JSON.stringify(shortcut.value),
|
|
110
|
+
shortcut.label,
|
|
111
|
+
checked,
|
|
112
|
+
idx
|
|
113
|
+
))
|
|
114
|
+
})
|
|
115
|
+
datePickerEl.parentElement.append(el)
|
|
116
|
+
datePickerEl.readOnly = true
|
|
117
|
+
datePickerEl.dataset['bsToggle'] = "dropdown"
|
|
118
|
+
new window.bootstrap5.Dropdown(datePickerEl)
|
|
119
|
+
}
|
|
120
|
+
|
|
59
121
|
initWidgets(parentEl=null) {
|
|
60
122
|
const datePickerSelector = {
|
|
61
123
|
'.js-datepicker': {
|
|
@@ -249,74 +249,100 @@ export const monthYearViewsPlugin = (fp) => {
|
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
+
export const createRadioInput = (id, name, value, label, checked, index) => {
|
|
253
|
+
const inputWrapperEl = document.createElement('label')
|
|
254
|
+
inputWrapperEl.classList.add('relative', 'block', 'px-12', 'py-8')
|
|
255
|
+
inputWrapperEl.setAttribute('for', id)
|
|
256
|
+
const labelEl = document.createElement('label')
|
|
257
|
+
labelEl.innerText = label
|
|
258
|
+
labelEl.setAttribute('for', id)
|
|
259
|
+
const inputEl = document.createElement('input')
|
|
260
|
+
inputEl.id = id
|
|
261
|
+
inputEl.name = name
|
|
262
|
+
inputEl.value = value
|
|
263
|
+
inputEl.type = 'radio'
|
|
264
|
+
inputEl.classList.add('radio', 'flatpickr-shortcut')
|
|
265
|
+
inputEl.checked = checked
|
|
266
|
+
inputEl.dataset["index"] = index
|
|
267
|
+
inputWrapperEl.append(inputEl)
|
|
268
|
+
inputWrapperEl.append(labelEl)
|
|
269
|
+
return inputWrapperEl
|
|
270
|
+
}
|
|
271
|
+
|
|
252
272
|
// eslint-disable-next-line no-unused-vars
|
|
253
273
|
export const customActionsPlugin = (fp) => {
|
|
254
|
-
|
|
255
|
-
const createRadioInput = (id, name, value, label, checked) => {
|
|
256
|
-
const inputWrapperEl = document.createElement('label')
|
|
257
|
-
inputWrapperEl.classList.add('relative', 'block', 'px-12', 'py-8')
|
|
258
|
-
inputWrapperEl.setAttribute('for', id)
|
|
259
|
-
const labelEl = document.createElement('label')
|
|
260
|
-
labelEl.innerText = label
|
|
261
|
-
labelEl.setAttribute('for', id)
|
|
262
|
-
const inputEl = document.createElement('input')
|
|
263
|
-
inputEl.id = id
|
|
264
|
-
inputEl.name = name
|
|
265
|
-
inputEl.value = value
|
|
266
|
-
inputEl.type = 'radio'
|
|
267
|
-
inputEl.classList.add('radio')
|
|
268
|
-
inputEl.checked = checked
|
|
269
|
-
inputWrapperEl.append(inputEl)
|
|
270
|
-
inputWrapperEl.append(labelEl)
|
|
271
|
-
return inputWrapperEl
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const dateTimeReviver = (key, value) => {
|
|
275
|
-
if (key === 'value') {
|
|
276
|
-
const newValue = []
|
|
277
|
-
value.forEach((val) => {
|
|
278
|
-
newValue.push(new Date(val))
|
|
279
|
-
})
|
|
280
|
-
return newValue
|
|
281
|
-
}
|
|
282
|
-
return value
|
|
283
|
-
}
|
|
284
|
-
|
|
285
274
|
const createShortcuts = () => {
|
|
286
|
-
const
|
|
287
|
-
const
|
|
275
|
+
const realInput = document.getElementById(fp.element.dataset.sbadminDatepickerRealInputId)
|
|
276
|
+
const baseId = realInput.id
|
|
277
|
+
const baseValue = realInput.value
|
|
288
278
|
const el = document.createElement('div')
|
|
289
|
-
const shortcuts = JSON.parse(fp.element.dataset.sbadminDatepickerShortcuts
|
|
279
|
+
const shortcuts = JSON.parse(fp.element.dataset.sbadminDatepickerShortcuts)
|
|
290
280
|
el.classList.add('flatpickr-shortcuts')
|
|
291
281
|
el.addEventListener('change', (e) => {
|
|
282
|
+
// on shortcut click/change set new value and label to real input element
|
|
292
283
|
const value = e.target.value
|
|
293
|
-
|
|
284
|
+
realInput.value = value
|
|
285
|
+
realInput.dataset['label'] = e.target.nextElementSibling.innerText
|
|
286
|
+
realInput.dispatchEvent(new Event('change'))
|
|
287
|
+
|
|
288
|
+
// parse new value and set it to flatpicker
|
|
289
|
+
const shortcutValue = shortcuts[e.target.dataset.index].value
|
|
290
|
+
const from = new Date()
|
|
291
|
+
from.setDate(from.getDate() + shortcutValue[0])
|
|
292
|
+
|
|
293
|
+
const to = new Date()
|
|
294
|
+
to.setDate(to.getDate() + shortcutValue[1])
|
|
295
|
+
|
|
296
|
+
fp.setDate([from, to], false, fp.config.dateFormat)
|
|
294
297
|
})
|
|
295
298
|
|
|
296
299
|
shortcuts.forEach((shortcut, idx) => {
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (fromDateFormatted === toDateFormatted) {
|
|
301
|
-
shortcutValueStr = fromDateFormatted
|
|
302
|
-
} else {
|
|
303
|
-
shortcutValueStr = fromDateFormatted + fp.config.locale.rangeSeparator + toDateFormatted
|
|
300
|
+
const checked = JSON.stringify(shortcut.value) === baseValue
|
|
301
|
+
if(checked) {
|
|
302
|
+
realInput.dataset['label'] = shortcut.label
|
|
304
303
|
}
|
|
305
304
|
el.append(createRadioInput(
|
|
306
305
|
`${baseId}_range${idx}`,
|
|
307
306
|
`${baseId}_shortcut`,
|
|
308
|
-
|
|
307
|
+
JSON.stringify(shortcut.value),
|
|
309
308
|
shortcut.label,
|
|
310
|
-
|
|
309
|
+
checked,
|
|
310
|
+
idx
|
|
311
311
|
))
|
|
312
312
|
})
|
|
313
313
|
fp.calendarContainer.prepend(el)
|
|
314
|
+
|
|
315
|
+
realInput.addEventListener('SBTableFilterFormLoad', () => {
|
|
316
|
+
try {
|
|
317
|
+
const shortcutValue = JSON.parse(realInput.value)
|
|
318
|
+
const from = new Date()
|
|
319
|
+
from.setDate(from.getDate() + shortcutValue[0])
|
|
320
|
+
|
|
321
|
+
const to = new Date()
|
|
322
|
+
to.setDate(to.getDate() + shortcutValue[1])
|
|
323
|
+
fp.setDate([from, to], false, fp.config.dateFormat)
|
|
324
|
+
|
|
325
|
+
shortcuts.forEach((shortcut, idx) => {
|
|
326
|
+
if(JSON.stringify(shortcut.value) === realInput.value) {
|
|
327
|
+
document.getElementById(`${baseId}_range${idx}`).checked = true
|
|
328
|
+
realInput.dataset['label'] = shortcut.label
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
fp.setDate(realInput.value, false, fp.config.dateFormat)
|
|
334
|
+
}
|
|
335
|
+
})
|
|
314
336
|
}
|
|
315
337
|
|
|
316
338
|
|
|
317
339
|
return {
|
|
318
340
|
onReady: createShortcuts,
|
|
341
|
+
onChange: (selectedDates, dateStr, instance) => {
|
|
342
|
+
const checkedShortcut = instance.element.parentElement.querySelector('input.flatpickr-shortcut:checked')
|
|
343
|
+
if(checkedShortcut){
|
|
344
|
+
checkedShortcut.checked = false
|
|
345
|
+
}
|
|
346
|
+
}
|
|
319
347
|
}
|
|
320
348
|
}
|
|
321
|
-
|
|
322
|
-
export const HIDE_CALENDAR_CLASS = 'hide-calendar' // used for hiding calendar and only use shortcuts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SBAdminTableModule } from "./base_module"
|
|
2
2
|
import { filterInputValueChangedUtil, filterInputValueChangeListener } from "../utils"
|
|
3
|
-
import {customActionsPlugin,
|
|
3
|
+
import {customActionsPlugin, monthYearViewsPlugin} from "../datepicker_plugins"
|
|
4
4
|
|
|
5
5
|
export class AdvancedFilterModule extends SBAdminTableModule {
|
|
6
6
|
constructor(table) {
|
|
@@ -104,6 +104,16 @@ export class AdvancedFilterModule extends SBAdminTableModule {
|
|
|
104
104
|
widgetEl._flatpickr.calendarContainer?.remove()
|
|
105
105
|
widgetEl._flatpickr.clear()
|
|
106
106
|
widgetEl._flatpickr.destroy()
|
|
107
|
+
widgetEl._flatpickr = undefined
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
const dropdownInstance = window.bootstrap5.Dropdown.getInstance(widgetEl)
|
|
111
|
+
if(dropdownInstance) {
|
|
112
|
+
dropdownInstance.dispose()
|
|
113
|
+
widgetEl.removeAttribute('data-bs-toggle')
|
|
114
|
+
widgetEl.nextElementSibling?.remove()
|
|
115
|
+
widgetEl.readOnly = false
|
|
116
|
+
widgetEl.value = ""
|
|
107
117
|
}
|
|
108
118
|
}
|
|
109
119
|
|
|
@@ -120,17 +130,14 @@ export class AdvancedFilterModule extends SBAdminTableModule {
|
|
|
120
130
|
if (["between", "not_between", "in_the_last", "in_the_next"].includes(rule.operator.type)) {
|
|
121
131
|
optionsOverride["mode"] = "range"
|
|
122
132
|
}
|
|
133
|
+
this.destroyDatePicker(widgetEl)
|
|
134
|
+
const shortcuts = JSON.parse(widgetEl.dataset.sbadminDatepickerShortcutsDict)
|
|
135
|
+
widgetEl.dataset.sbadminDatepickerShortcuts = JSON.stringify(shortcuts[rule.operator.type] || [])
|
|
123
136
|
if (["in_the_last", "in_the_next"].includes(rule.operator.type)) {
|
|
124
|
-
|
|
125
|
-
// optionsOverride["noCalendar"] = true
|
|
126
|
-
widgetEl.classList.add(HIDE_CALENDAR_CLASS)
|
|
137
|
+
window.SBAdmin.datepicker.initShortcutsDropdown(widgetEl, {}, optionsOverride)
|
|
127
138
|
}
|
|
128
139
|
else {
|
|
129
|
-
|
|
140
|
+
window.SBAdmin.datepicker.initFlatPickr(widgetEl, {}, optionsOverride)
|
|
130
141
|
}
|
|
131
|
-
this.destroyDatePicker(widgetEl)
|
|
132
|
-
const shortcuts = JSON.parse(widgetEl.dataset.sbadminDatepickerShortcutsDict)
|
|
133
|
-
widgetEl.dataset.sbadminDatepickerShortcuts = JSON.stringify(shortcuts[rule.operator.type] || [])
|
|
134
|
-
window.SBAdmin.datepicker.initFlatPickr(widgetEl, {}, optionsOverride)
|
|
135
142
|
}
|
|
136
143
|
}
|
|
@@ -73,6 +73,11 @@ export const filterInputValueChangedUtil = (field) => {
|
|
|
73
73
|
if(!valueElem) {
|
|
74
74
|
return
|
|
75
75
|
}
|
|
76
|
+
const label = field.dataset.label
|
|
77
|
+
if(label) {
|
|
78
|
+
valueElem.innerHTML = label
|
|
79
|
+
return valueElem
|
|
80
|
+
}
|
|
76
81
|
const valueOrObject = getObjectOrValue(field.value)
|
|
77
82
|
if ((field.value === "" || field.value === "[]")) {
|
|
78
83
|
if(field.dataset.emptyLabel) {
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
<div class="relative">
|
|
2
2
|
<input
|
|
3
|
-
type="
|
|
3
|
+
type="hidden"
|
|
4
4
|
autocomplete="off"
|
|
5
|
-
class="input pl-10 js-datepicker-dynamic"
|
|
6
5
|
id="{{ filter_widget.input_id }}"
|
|
7
6
|
name="{{ filter_widget.input_name }}"
|
|
7
|
+
>
|
|
8
|
+
<input
|
|
9
|
+
type="text"
|
|
10
|
+
autocomplete="off"
|
|
11
|
+
class="input pl-10 js-datepicker-dynamic"
|
|
12
|
+
id="{{ filter_widget.input_id }}_picker"
|
|
13
|
+
name="{{ filter_widget.input_name }}_picker"
|
|
14
|
+
data-sbadmin-datepicker-real-input-id="{{ filter_widget.input_id }}"
|
|
8
15
|
data-sbadmin-datepicker="{{ filter_widget.get_data }}"
|
|
9
16
|
data-sbadmin-datepicker-shortcuts-dict="{{ filter_widget.get_shortcuts_dict_data }}"
|
|
10
17
|
>
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
<div class="dropdown-menu dropdown-menu-datepicker"
|
|
2
2
|
style="max-width: none; width: auto; height: auto; max-height: none;">
|
|
3
3
|
<div class="px-12 py-8">
|
|
4
|
-
<input
|
|
5
|
-
|
|
4
|
+
<input
|
|
5
|
+
type="hidden"
|
|
6
|
+
autocomplete="off"
|
|
7
|
+
id="{{ filter_widget.input_id }}"
|
|
8
|
+
name="{{ filter_widget.input_name }}"
|
|
9
|
+
form="{{ filter_widget.view_id }}-filter-form"
|
|
10
|
+
{% if filter_widget.get_default_value %}value="{{ filter_widget.get_default_value }}"{% endif %}
|
|
11
|
+
{% if not all_filters_visible %}disabled{% endif %}
|
|
12
|
+
>
|
|
13
|
+
<input type="text"
|
|
14
|
+
class="input pl-10 js-datepicker-range"
|
|
15
|
+
id="{{ filter_widget.input_id }}_picker"
|
|
16
|
+
name="{{ filter_widget.input_name }}_picker"
|
|
17
|
+
data-sbadmin-datepicker-real-input-id="{{ filter_widget.input_id }}"
|
|
6
18
|
data-sbadmin-datepicker="{{ filter_widget.get_data }}"
|
|
7
|
-
data-sbadmin-datepicker-shortcuts="{{ filter_widget.get_shortcuts_data }}"
|
|
8
|
-
{% if not all_filters_visible %}disabled{% endif %}{% if filter_widget.get_default_value %}
|
|
9
|
-
value="{{ filter_widget.get_default_value }}"{% endif %}>
|
|
19
|
+
data-sbadmin-datepicker-shortcuts="{{ filter_widget.get_shortcuts_data }}">
|
|
10
20
|
</div>
|
|
11
21
|
{% include "sb_admin/filter_widgets/partials/clear.html" %}
|
|
12
22
|
</div>
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<div class="flex max-xs:flex-wrap gap-x-16">{% endif %}
|
|
24
24
|
|
|
25
25
|
{% for field in line %}
|
|
26
|
-
<div class="mb-16 sm:mb-24 max-xs:w-full sm:flex-1{% if field.field.is_hidden %} hidden{% endif %}">
|
|
26
|
+
<div class="mb-16 sm:mb-24 max-xs:w-full sm:flex-1{% if field.field.is_hidden or field.field|is_row_class_field %} hidden{% endif %}">
|
|
27
27
|
{% if field.is_readonly %}
|
|
28
28
|
{% call_method field "contents" request %}
|
|
29
29
|
{% else %}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{% load widget_tweaks %}
|
|
2
2
|
{% if inline_admin_formset.formset.can_delete %}
|
|
3
3
|
{% if inline_admin_form.original %}
|
|
4
|
-
<td class="w-40">
|
|
4
|
+
<td class="w-40{% if sticky %} sticky-table-col{% endif %}">
|
|
5
5
|
<div class="relative min-h-40 flex items-center delete djn-delete-handler {{ inline_admin_formset.handler_classes|join:" " }}">
|
|
6
6
|
{% render_field inline_admin_form.deletion_field.field class="checkbox checkbox-delete" %}
|
|
7
7
|
<label for="{{ inline_admin_form.deletion_field.field.auto_id }}">
|
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
</div>
|
|
13
13
|
</td>
|
|
14
14
|
{% else %}
|
|
15
|
-
<td class="delete w-40">
|
|
16
|
-
<a
|
|
17
|
-
|
|
18
|
-
href="javascript:void(0)">
|
|
15
|
+
<td class="delete w-40{% if sticky %} sticky-table-col{% endif %}">
|
|
16
|
+
<a class="flex-center h-40 inline-deletelink djn-remove-handler {{ inline_admin_formset.handler_classes|join:" " }}"
|
|
17
|
+
href="javascript:void(0)">
|
|
19
18
|
<svg class="w-20 h-20 text-dark-300 hover:text-dark-400">
|
|
20
19
|
<use xlink:href="#Delete"></use>
|
|
21
20
|
</svg>
|
|
@@ -70,8 +70,11 @@
|
|
|
70
70
|
</th>
|
|
71
71
|
{% endif %}
|
|
72
72
|
{% endfor %}
|
|
73
|
-
{%
|
|
74
|
-
|
|
73
|
+
{% block table_inline_delete_table_head %}
|
|
74
|
+
{% if inline_admin_formset.formset.can_delete %}
|
|
75
|
+
<th class="djn-th sticky-table-head" style="min-width: 54px; width: 54px;"></th>
|
|
76
|
+
{% endif %}
|
|
77
|
+
{% endblock %}
|
|
75
78
|
</tr>
|
|
76
79
|
</thead>
|
|
77
80
|
|
|
@@ -163,7 +166,7 @@
|
|
|
163
166
|
{% endfor %}
|
|
164
167
|
{% endfor %}
|
|
165
168
|
{% block table_inline_delete_button %}
|
|
166
|
-
{% include "sb_admin/includes/table_inline_delete_button.html" %}
|
|
169
|
+
{% include "sb_admin/includes/table_inline_delete_button.html" with sticky=True %}
|
|
167
170
|
{% endblock %}
|
|
168
171
|
</tr>
|
|
169
172
|
{% block table_inline_after_line %}{% endblock %}
|
|
@@ -183,6 +186,21 @@
|
|
|
183
186
|
{% endblock %}
|
|
184
187
|
</table>
|
|
185
188
|
|
|
189
|
+
{% block table_script %}
|
|
190
|
+
<script>
|
|
191
|
+
{# move all non initial lines to the top of the table to maintain original order #}
|
|
192
|
+
{# this should happen only during validation error #}
|
|
193
|
+
(function(){
|
|
194
|
+
const group = document.getElementById('{{ inline_admin_formset.formset.prefix }}-group');
|
|
195
|
+
const table = group.querySelector('.djn-table');
|
|
196
|
+
const bodies_to_move = Array.from(group.querySelectorAll('.djn-tbody[data-is-initial="false"]'));
|
|
197
|
+
if(bodies_to_move) {
|
|
198
|
+
table.prepend(...bodies_to_move.reverse());
|
|
199
|
+
}
|
|
200
|
+
}());
|
|
201
|
+
</script>
|
|
202
|
+
{% endblock %}
|
|
203
|
+
|
|
186
204
|
</fieldset>
|
|
187
205
|
</div>
|
|
188
206
|
|
|
@@ -161,7 +161,7 @@ class ModelTranslationView(SBAdminView, SBAdminBaseListView):
|
|
|
161
161
|
annotate=Case(
|
|
162
162
|
When(
|
|
163
163
|
**{
|
|
164
|
-
f"{annotate_name}
|
|
164
|
+
f"{annotate_name}_count__gte": F(
|
|
165
165
|
f"{main_lang_annotate_name}_count"
|
|
166
166
|
),
|
|
167
167
|
"then": Value(self.TRANSLATION_TRANSLATED),
|