django-smartbase-admin 1.0.14__py3-none-any.whl → 1.0.15__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 +14 -14
- django_smartbase_admin/admin/site.py +7 -1
- django_smartbase_admin/admin/widgets.py +13 -1
- django_smartbase_admin/engine/admin_base_view.py +14 -0
- django_smartbase_admin/locale/sk/LC_MESSAGES/django.mo +0 -0
- django_smartbase_admin/locale/sk/LC_MESSAGES/django.po +22 -13
- django_smartbase_admin/migrations/0005_sbadminuserconfiguration.py +26 -0
- django_smartbase_admin/models.py +33 -0
- django_smartbase_admin/services/configuration.py +13 -0
- django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/colors.js +4 -0
- django_smartbase_admin/static/sb_admin/css/ckeditor/ckeditor_content_dark.css +208 -0
- django_smartbase_admin/static/sb_admin/dist/calendar.js +1 -1
- django_smartbase_admin/static/sb_admin/dist/calendar_style.css +1 -1
- 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/sprites/sb_admin/Moon.svg +3 -0
- django_smartbase_admin/static/sb_admin/sprites/sb_admin/Sun-one.svg +3 -0
- django_smartbase_admin/static/sb_admin/src/css/_base.css +1 -1
- django_smartbase_admin/static/sb_admin/src/css/_colors.css +257 -82
- django_smartbase_admin/static/sb_admin/src/css/_components.css +1 -1
- django_smartbase_admin/static/sb_admin/src/css/_tabulator.css +2 -2
- django_smartbase_admin/static/sb_admin/src/css/calendar.css +3 -3
- django_smartbase_admin/static/sb_admin/src/css/components/_button.css +1 -1
- django_smartbase_admin/static/sb_admin/src/css/components/_dropdown.css +25 -7
- django_smartbase_admin/static/sb_admin/src/css/components/_input.css +33 -2
- django_smartbase_admin/static/sb_admin/src/css/components/_modal.css +1 -1
- django_smartbase_admin/static/sb_admin/src/css/components/_query-builder.css +1 -7
- django_smartbase_admin/static/sb_admin/src/css/style.css +1 -1
- django_smartbase_admin/static/sb_admin/src/js/main.js +58 -15
- django_smartbase_admin/static/sb_admin/src/js/multiselect.js +10 -19
- django_smartbase_admin/static/sb_admin/src/js/utils.js +47 -23
- django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/multiple_choice_field.html +1 -1
- django_smartbase_admin/templates/sb_admin/filter_widgets/radio_choice_field.html +1 -1
- django_smartbase_admin/templates/sb_admin/navigation.html +9 -7
- django_smartbase_admin/templates/sb_admin/sb_admin_base_no_sidebar.html +1 -1
- django_smartbase_admin/templates/sb_admin/sprites/sb_admin.svg +1 -1
- django_smartbase_admin/templates/sb_admin/tailwind_whitelist.html +2 -1
- django_smartbase_admin/templates/sb_admin/widgets/{checkbox_select.html → checkbox_dropdown.html} +2 -2
- django_smartbase_admin/templates/sb_admin/widgets/radio.html +3 -2
- django_smartbase_admin/templates/sb_admin/widgets/radio_dropdown.html +30 -0
- django_smartbase_admin/views/user_config_view.py +35 -0
- {django_smartbase_admin-1.0.14.dist-info → django_smartbase_admin-1.0.15.dist-info}/METADATA +1 -1
- {django_smartbase_admin-1.0.14.dist-info → django_smartbase_admin-1.0.15.dist-info}/RECORD +47 -41
- {django_smartbase_admin-1.0.14.dist-info → django_smartbase_admin-1.0.15.dist-info}/LICENSE.md +0 -0
- {django_smartbase_admin-1.0.14.dist-info → django_smartbase_admin-1.0.15.dist-info}/WHEEL +0 -0
|
@@ -27,12 +27,13 @@ import Range from "./range"
|
|
|
27
27
|
import Sorting from "./sorting"
|
|
28
28
|
import Autocomplete from "./autocomplete"
|
|
29
29
|
import ChoicesJS from "./choices"
|
|
30
|
-
import {setCookie} from "./utils"
|
|
30
|
+
import {setCookie, setDropdownLabel} from "./utils"
|
|
31
31
|
import Multiselect from "./multiselect"
|
|
32
32
|
|
|
33
33
|
class Main {
|
|
34
34
|
constructor() {
|
|
35
35
|
document.body.classList.add('js-ready')
|
|
36
|
+
this.handleColorSchemeChange()
|
|
36
37
|
|
|
37
38
|
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
|
38
39
|
tooltipTriggerList.map((tooltipTriggerEl) => {
|
|
@@ -104,6 +105,22 @@ class Main {
|
|
|
104
105
|
this.handleLocationHashFromTabs()
|
|
105
106
|
}
|
|
106
107
|
|
|
108
|
+
handleColorSchemeChange() {
|
|
109
|
+
const picker = document.querySelector('.js-color-scheme-picker')
|
|
110
|
+
if(!picker) {
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
picker.addEventListener('change', (e)=>{
|
|
114
|
+
if(e.target.value) {
|
|
115
|
+
document.documentElement.setAttribute('data-theme', e.target.value)
|
|
116
|
+
this.switchCKEditorTheme(e.target.value)
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
document.documentElement.removeAttribute('data-theme')
|
|
120
|
+
})
|
|
121
|
+
this.switchCKEditorTheme(document.documentElement.dataset.theme)
|
|
122
|
+
}
|
|
123
|
+
|
|
107
124
|
initInlines(target) {
|
|
108
125
|
target = target || document
|
|
109
126
|
const inlineGroups = target.querySelectorAll('.inline-group')
|
|
@@ -173,7 +190,7 @@ class Main {
|
|
|
173
190
|
} else {
|
|
174
191
|
offset = [0, 8]
|
|
175
192
|
}
|
|
176
|
-
|
|
193
|
+
const dropdown = new Dropdown(dropdownToggleEl, {
|
|
177
194
|
autoClose: 'outside',
|
|
178
195
|
offset: offset,
|
|
179
196
|
popperConfig(defaultBsPopperConfig) {
|
|
@@ -184,6 +201,14 @@ class Main {
|
|
|
184
201
|
return {...defaultBsPopperConfig, ...elementConf, strategy: 'fixed'}
|
|
185
202
|
}
|
|
186
203
|
})
|
|
204
|
+
const dropdownWrapper = dropdownToggleEl.closest('.js-dropdown-wrapper')
|
|
205
|
+
if(dropdownWrapper) {
|
|
206
|
+
const dropdownLabelEl = dropdownWrapper.querySelector('.js-dropdown-label')
|
|
207
|
+
dropdown._menu.addEventListener('change', ()=>{
|
|
208
|
+
setDropdownLabel(dropdown._menu, dropdownLabelEl)
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
return dropdown
|
|
187
212
|
})
|
|
188
213
|
}
|
|
189
214
|
|
|
@@ -304,28 +329,46 @@ class Main {
|
|
|
304
329
|
})
|
|
305
330
|
}
|
|
306
331
|
|
|
307
|
-
initCKEditor(target) {
|
|
332
|
+
initCKEditor(target, config, force=false) {
|
|
308
333
|
if (!window.CKEDITOR) {
|
|
309
334
|
return
|
|
310
335
|
}
|
|
311
336
|
target = target || document
|
|
312
337
|
target.querySelectorAll('textarea[data-type="ckeditortype"]').forEach((textarea) => {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
return
|
|
316
|
-
}
|
|
317
|
-
if(
|
|
318
|
-
textarea.getAttribute("data-processed") == "0" &&
|
|
319
|
-
textarea.id.indexOf("__prefix__") == -1
|
|
320
|
-
) {
|
|
321
|
-
if(window.CKEDITOR.instances[id]) {
|
|
322
|
-
window.CKEDITOR.instances[id].destroy(true)
|
|
323
|
-
}
|
|
324
|
-
window.CKEDITOR.replace(id, JSON.parse(textarea.getAttribute("data-config")))
|
|
338
|
+
if( force || (textarea.getAttribute("data-processed") == "0" && textarea.id.indexOf("__prefix__") == -1)) {
|
|
339
|
+
this.reinitCKEditor(textarea, config)
|
|
325
340
|
}
|
|
326
341
|
})
|
|
327
342
|
}
|
|
328
343
|
|
|
344
|
+
reinitCKEditor(textarea, config) {
|
|
345
|
+
const id = textarea.id
|
|
346
|
+
if (!id) {
|
|
347
|
+
return
|
|
348
|
+
}
|
|
349
|
+
if(window.CKEDITOR.instances[id]) {
|
|
350
|
+
window.CKEDITOR.instances[id].destroy(true)
|
|
351
|
+
}
|
|
352
|
+
config = config || {}
|
|
353
|
+
const new_config = {...JSON.parse(textarea.getAttribute("data-config")), ...config}
|
|
354
|
+
window.CKEDITOR.replace(id, new_config)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
switchCKEditorTheme(colorScheme) {
|
|
358
|
+
if(!window.CKEDITOR) {
|
|
359
|
+
return
|
|
360
|
+
}
|
|
361
|
+
let dark = colorScheme === 'dark'
|
|
362
|
+
if(colorScheme === 'auto') {
|
|
363
|
+
dark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
364
|
+
}
|
|
365
|
+
if(dark) {
|
|
366
|
+
this.initCKEditor(document, {'contentsCss': '/static/sb_admin/css/ckeditor/ckeditor_content_dark.css', uiColor: '#000000'}, true)
|
|
367
|
+
return
|
|
368
|
+
}
|
|
369
|
+
this.initCKEditor(document, {'contentsCss':window.CKEDITOR.config.contentsCss}, true)
|
|
370
|
+
}
|
|
371
|
+
|
|
329
372
|
clearFilter(inputId) {
|
|
330
373
|
const fieldElem = document.querySelector(`#${inputId}`)
|
|
331
374
|
fieldElem.value = ''
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {setDropdownLabel} from "./utils"
|
|
2
|
+
|
|
1
3
|
export default class Multiselect {
|
|
2
4
|
constructor(selector_override, options_override, target) {
|
|
3
5
|
target = target || document
|
|
@@ -9,9 +11,13 @@ export default class Multiselect {
|
|
|
9
11
|
document.addEventListener('change', e => {
|
|
10
12
|
const wrapperEl = e.target.closest(this.wrapperSelector)
|
|
11
13
|
const multiselectInput = wrapperEl?.querySelector(selector)
|
|
14
|
+
if(!wrapperEl || !multiselectInput) {
|
|
15
|
+
// not filter widget
|
|
16
|
+
return
|
|
17
|
+
}
|
|
12
18
|
const isCheckboxClicked = e.target.type === 'checkbox'
|
|
13
19
|
const selectAllEl = wrapperEl?.querySelector(`.${selectAllClass}`)
|
|
14
|
-
if(
|
|
20
|
+
if(isCheckboxClicked) {
|
|
15
21
|
const checkboxes = Array.from(this.getCheckboxes(multiselectInput))
|
|
16
22
|
if(e.target.classList.contains(selectAllClass)) {
|
|
17
23
|
checkboxes.forEach(el => {
|
|
@@ -79,35 +85,20 @@ export default class Multiselect {
|
|
|
79
85
|
})
|
|
80
86
|
}
|
|
81
87
|
|
|
82
|
-
|
|
83
|
-
setLabel(wrapper, valueEl) {
|
|
84
|
-
let labels = []
|
|
85
|
-
wrapper.querySelectorAll('input[type="checkbox"]').forEach(el => {
|
|
86
|
-
if (el.checked) {
|
|
87
|
-
labels.push(document.querySelector(`label[for="${el.id}"]`).innerText)
|
|
88
|
-
}
|
|
89
|
-
})
|
|
90
|
-
valueEl.innerHTML = labels.join(',')
|
|
91
|
-
}
|
|
92
|
-
|
|
93
88
|
clearAll(wrapper, valueEl) {
|
|
94
89
|
wrapper.querySelectorAll('input[type="checkbox"]').forEach(el => {
|
|
95
90
|
el.checked = false
|
|
96
91
|
})
|
|
97
|
-
|
|
92
|
+
setDropdownLabel(wrapper, valueEl)
|
|
98
93
|
}
|
|
99
94
|
|
|
100
95
|
|
|
101
96
|
initDetailMultiselect(wrapper) {
|
|
102
|
-
const valueEl = wrapper.querySelector('.js-value')
|
|
103
97
|
const clearEl = wrapper.querySelector('.js-clear')
|
|
104
98
|
|
|
105
|
-
wrapper.addEventListener('change', () => {
|
|
106
|
-
this.setLabel(wrapper, valueEl)
|
|
107
|
-
})
|
|
108
99
|
clearEl?.addEventListener('click', () => {
|
|
109
|
-
this.clearAll(wrapper
|
|
100
|
+
this.clearAll(wrapper)
|
|
110
101
|
})
|
|
111
|
-
|
|
102
|
+
setDropdownLabel(wrapper)
|
|
112
103
|
}
|
|
113
104
|
}
|
|
@@ -66,6 +66,52 @@ export const filterInputValueChangeListener = (inputSelector, callbackFunction)
|
|
|
66
66
|
})
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
const getResultLabel = (valueOrObject, separator=', ') => {
|
|
70
|
+
const labelArray = []
|
|
71
|
+
const entries = Object.values(valueOrObject)
|
|
72
|
+
let hasMaxEntries = false
|
|
73
|
+
for (let [index, item] of entries.entries()) {
|
|
74
|
+
if (index === window.sb_admin_const.MULTISELECT_FILTER_MAX_CHOICES_SHOWN) {
|
|
75
|
+
break
|
|
76
|
+
}
|
|
77
|
+
if(entries.length > 1 && item.value === window.sb_admin_const.SELECT_ALL_KEYWORD) {
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
if (index === window.sb_admin_const.MULTISELECT_FILTER_MAX_CHOICES_SHOWN - 2 && entries[index + 2]) {
|
|
81
|
+
labelArray.push(item.label)
|
|
82
|
+
hasMaxEntries = true
|
|
83
|
+
break
|
|
84
|
+
}
|
|
85
|
+
labelArray.push(item.label)
|
|
86
|
+
}
|
|
87
|
+
let resultLabel = labelArray.join(separator)
|
|
88
|
+
if(hasMaxEntries) {
|
|
89
|
+
resultLabel = resultLabel.substring(0, resultLabel.length)
|
|
90
|
+
resultLabel += `... +${entries.length - window.sb_admin_const.MULTISELECT_FILTER_MAX_CHOICES_SHOWN + 1}`
|
|
91
|
+
}
|
|
92
|
+
return resultLabel
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const setDropdownLabel = (dropdownMenuEl, dropdownLabelEl) => {
|
|
96
|
+
if(!dropdownMenuEl) {
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
if(!dropdownLabelEl) {
|
|
100
|
+
dropdownLabelEl = dropdownMenuEl.querySelector('.js-dropdown-label')
|
|
101
|
+
}
|
|
102
|
+
if(!dropdownLabelEl) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
const fields = Array.from(dropdownMenuEl.querySelectorAll('input[type="checkbox"]:checked, input[type="radio"]:checked')).map(el => {
|
|
106
|
+
const label = el.parentElement.querySelector(`label[for="${el.id}"]`)
|
|
107
|
+
return {
|
|
108
|
+
'value': el.value,
|
|
109
|
+
'label': label?label.innerHTML:''
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
dropdownLabelEl.innerHTML = getResultLabel(fields)
|
|
113
|
+
}
|
|
114
|
+
|
|
69
115
|
export const filterInputValueChangedUtil = (field) => {
|
|
70
116
|
const filterId = field.dataset.filterId || field.id
|
|
71
117
|
const separator = field.dataset.labelSeparator || ', '
|
|
@@ -88,29 +134,7 @@ export const filterInputValueChangedUtil = (field) => {
|
|
|
88
134
|
return valueElem
|
|
89
135
|
}
|
|
90
136
|
if (typeof valueOrObject === 'object') {
|
|
91
|
-
|
|
92
|
-
const entries = Object.values(valueOrObject)
|
|
93
|
-
let hasMaxEntries = false
|
|
94
|
-
for (let [index, item] of entries.entries()) {
|
|
95
|
-
if (index === window.sb_admin_const.MULTISELECT_FILTER_MAX_CHOICES_SHOWN) {
|
|
96
|
-
break
|
|
97
|
-
}
|
|
98
|
-
if(entries.length > 1 && item.value === window.sb_admin_const.SELECT_ALL_KEYWORD) {
|
|
99
|
-
continue
|
|
100
|
-
}
|
|
101
|
-
if (index === window.sb_admin_const.MULTISELECT_FILTER_MAX_CHOICES_SHOWN - 2 && entries[index + 2]) {
|
|
102
|
-
labelArray.push(item.label)
|
|
103
|
-
hasMaxEntries = true
|
|
104
|
-
break
|
|
105
|
-
}
|
|
106
|
-
labelArray.push(item.label)
|
|
107
|
-
}
|
|
108
|
-
let resultLabel = labelArray.join(separator)
|
|
109
|
-
if(hasMaxEntries) {
|
|
110
|
-
resultLabel = resultLabel.substring(0, resultLabel.length)
|
|
111
|
-
resultLabel += `... +${entries.length - window.sb_admin_const.MULTISELECT_FILTER_MAX_CHOICES_SHOWN + 1}`
|
|
112
|
-
}
|
|
113
|
-
valueElem.innerHTML = resultLabel
|
|
137
|
+
valueElem.innerHTML = getResultLabel(valueOrObject, separator)
|
|
114
138
|
} else {
|
|
115
139
|
try {
|
|
116
140
|
// select
|
django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/multiple_choice_field.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
data-bs-auto-close="outside"
|
|
8
8
|
class="btn !rounded px-10 font-normal w-full"
|
|
9
9
|
>
|
|
10
|
-
<span id="{{ filter_widget.input_id }}-value"
|
|
10
|
+
<span id="{{ filter_widget.input_id }}-value"></span>
|
|
11
11
|
<svg class="ml-8">
|
|
12
12
|
<use xlink:href="#Down"></use>
|
|
13
13
|
</svg>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
{% for choice in filter_widget.choices %}
|
|
8
8
|
<li>
|
|
9
9
|
<div class="relative">
|
|
10
|
-
<input onchange="document.getElementById('{{ filter_widget.input_id }}').value = event.target.value;document.getElementById('{{ filter_widget.input_id }}').dispatchEvent(new Event('change'))" type="radio" value="{{ choice.0 }}" name="{{ filter_widget.input_name }}_dummy" class="radio" id="{{ filter_widget.input_id }}_{{ choice.0 }}" {% if filter_widget.get_default_value == choice.0 %} checked{% endif %} data-label="{{ choice.1 }}">
|
|
10
|
+
<input onchange="document.getElementById('{{ filter_widget.input_id }}').value = event.target.value;document.getElementById('{{ filter_widget.input_id }}').dispatchEvent(new Event('change'))" type="radio" value="{{ choice.0 }}" name="{{ filter_widget.input_name }}_dummy" class="radio radio-list" id="{{ filter_widget.input_id }}_{{ choice.0 }}" {% if filter_widget.get_default_value == choice.0 %} checked{% endif %} data-label="{{ choice.1 }}">
|
|
11
11
|
<label for="{{ filter_widget.input_id }}_{{ choice.0 }}">
|
|
12
12
|
{{ choice.1|safe }}
|
|
13
13
|
</label>
|
|
@@ -148,16 +148,18 @@
|
|
|
148
148
|
</svg>
|
|
149
149
|
{% trans 'Change password' %}
|
|
150
150
|
</li>
|
|
151
|
-
{%
|
|
152
|
-
<li class="
|
|
153
|
-
{%
|
|
151
|
+
{% block color_scheme %}
|
|
152
|
+
<li class="border-t border-dark-100 pt-8">
|
|
153
|
+
<form hx-post="{% url 'sb_admin:color_scheme' %}" hx-trigger="change" hx-swap="none" id="color-schema-form" class="js-color-scheme-picker">
|
|
154
|
+
{{ color_scheme_form.color_scheme }}
|
|
155
|
+
</form>
|
|
154
156
|
</li>
|
|
155
|
-
|
|
157
|
+
{% endblock %}
|
|
158
|
+
{% if request_data.global_filter_instance %}
|
|
159
|
+
<li class="border-t border-dark-100 pt-8">
|
|
156
160
|
<form hx-post="{% url 'sb_admin:global_filter' %}" hx-trigger="change" hx-swap="none" id="global_filter_form">
|
|
157
161
|
{% for field in request_data.global_filter_instance %}
|
|
158
|
-
|
|
159
|
-
{{ field }}
|
|
160
|
-
</div>
|
|
162
|
+
{{ field }}
|
|
161
163
|
{{ field.errors }}
|
|
162
164
|
{% endfor %}
|
|
163
165
|
</form>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{% load static %}
|
|
2
2
|
|
|
3
3
|
<!DOCTYPE html>
|
|
4
|
-
<html lang="{{ request.LANGUAGE_CODE }}" data-theme="
|
|
4
|
+
<html lang="{{ request.LANGUAGE_CODE }}" data-theme="{{ user_config.color_scheme }}">
|
|
5
5
|
<head>
|
|
6
6
|
{% include 'sb_admin/fonts.html' %}
|
|
7
7
|
<meta name="robots" content="noindex"/>
|