django-smartbase-admin 0.2.54__py3-none-any.whl → 1.0.42__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 (184) hide show
  1. django_smartbase_admin/actions/admin_action_list.py +79 -38
  2. django_smartbase_admin/actions/advanced_filters.py +24 -1
  3. django_smartbase_admin/admin/admin_base.py +402 -97
  4. django_smartbase_admin/admin/site.py +93 -35
  5. django_smartbase_admin/admin/widgets.py +636 -26
  6. django_smartbase_admin/apps.py +2 -0
  7. django_smartbase_admin/engine/actions.py +34 -16
  8. django_smartbase_admin/engine/admin_base_view.py +252 -115
  9. django_smartbase_admin/engine/configuration.py +186 -4
  10. django_smartbase_admin/engine/const.py +6 -0
  11. django_smartbase_admin/engine/dashboard.py +49 -24
  12. django_smartbase_admin/engine/fake_inline.py +15 -11
  13. django_smartbase_admin/engine/field.py +42 -12
  14. django_smartbase_admin/engine/field_formatter.py +38 -14
  15. django_smartbase_admin/engine/filter_widgets.py +348 -24
  16. django_smartbase_admin/engine/menu_item.py +8 -5
  17. django_smartbase_admin/engine/modal_view.py +12 -7
  18. django_smartbase_admin/engine/request.py +2 -0
  19. django_smartbase_admin/integration/__init__.py +0 -0
  20. django_smartbase_admin/integration/django_cms.py +43 -0
  21. django_smartbase_admin/locale/sk/LC_MESSAGES/django.mo +0 -0
  22. django_smartbase_admin/locale/sk/LC_MESSAGES/django.po +268 -37
  23. django_smartbase_admin/migrations/0005_sbadminuserconfiguration.py +26 -0
  24. django_smartbase_admin/migrations/0006_alter_sbadminuserconfiguration_color_scheme.py +18 -0
  25. django_smartbase_admin/models.py +22 -0
  26. django_smartbase_admin/monkeypatch/admin_readonly_field_monkeypatch.py +96 -0
  27. django_smartbase_admin/monkeypatch/fake_inline_monkeypatch.py +1 -1
  28. django_smartbase_admin/querysets.py +3 -0
  29. django_smartbase_admin/services/configuration.py +30 -0
  30. django_smartbase_admin/services/thread_local.py +6 -19
  31. django_smartbase_admin/services/views.py +80 -13
  32. django_smartbase_admin/services/xlsx_export.py +6 -0
  33. django_smartbase_admin/static/sb_admin/build/tailwind.config.js +1 -0
  34. django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/colors.js +4 -0
  35. django_smartbase_admin/static/sb_admin/build/tailwind_config_partials/spacing.js +1 -0
  36. django_smartbase_admin/static/sb_admin/build/webpack.common.js +11 -8
  37. django_smartbase_admin/static/sb_admin/css/ckeditor/ckeditor_content_dark.css +208 -0
  38. django_smartbase_admin/static/sb_admin/css/coloris/coloris.min.css +1 -0
  39. django_smartbase_admin/static/sb_admin/dist/calendar.js +1 -0
  40. django_smartbase_admin/static/sb_admin/dist/calendar_style.css +1 -0
  41. django_smartbase_admin/static/sb_admin/dist/calendar_style.js +0 -0
  42. django_smartbase_admin/static/sb_admin/dist/chart.js +1 -1
  43. django_smartbase_admin/static/sb_admin/dist/main.js +1 -1
  44. django_smartbase_admin/static/sb_admin/dist/main_style.css +1 -1
  45. django_smartbase_admin/static/sb_admin/dist/table.js +1 -1
  46. django_smartbase_admin/static/sb_admin/dist/tree_widget.js +1 -0
  47. django_smartbase_admin/static/sb_admin/dist/tree_widget_style.css +1 -0
  48. django_smartbase_admin/static/sb_admin/dist/tree_widget_style.js +0 -0
  49. django_smartbase_admin/static/sb_admin/fancytree/jquery.fancytree-all-deps.min.js +1 -0
  50. django_smartbase_admin/static/sb_admin/images/file_types/file-csv.svg +11 -0
  51. django_smartbase_admin/static/sb_admin/images/file_types/file-doc.svg +11 -0
  52. django_smartbase_admin/static/sb_admin/images/file_types/file-docx.svg +11 -0
  53. django_smartbase_admin/static/sb_admin/images/file_types/file-other.svg +13 -0
  54. django_smartbase_admin/static/sb_admin/images/file_types/file-pdf.svg +11 -0
  55. django_smartbase_admin/static/sb_admin/images/file_types/file-ppt.svg +11 -0
  56. django_smartbase_admin/static/sb_admin/images/file_types/file-xls.svg +11 -0
  57. django_smartbase_admin/static/sb_admin/images/file_types/file-xlsx.svg +11 -0
  58. django_smartbase_admin/static/sb_admin/images/file_types/file-zip.svg +18 -0
  59. django_smartbase_admin/static/sb_admin/images/flags/de-at.png +0 -0
  60. django_smartbase_admin/static/sb_admin/images/flags/de-ch.png +0 -0
  61. django_smartbase_admin/static/sb_admin/images/logo_light.svg +21 -0
  62. django_smartbase_admin/static/sb_admin/js/coloris/coloris.min.js +6 -0
  63. django_smartbase_admin/static/sb_admin/js/fullcalendar.min.js +14804 -0
  64. django_smartbase_admin/static/sb_admin/js/sbadmin_prepopulated_fields_init.js +25 -0
  65. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Bolt-one.svg +3 -0
  66. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Calendar.svg +3 -0
  67. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Caution.svg +3 -0
  68. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Electric-drill.svg +3 -0
  69. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Fire-extinguisher.svg +3 -0
  70. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Gas.svg +3 -0
  71. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Lightning-fill.svg +3 -0
  72. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Moon.svg +3 -0
  73. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Phone-telephone.svg +3 -0
  74. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Printer.svg +3 -0
  75. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Pull.svg +3 -0
  76. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Sun-one.svg +3 -0
  77. django_smartbase_admin/static/sb_admin/sprites/sb_admin/Time.svg +3 -0
  78. django_smartbase_admin/static/sb_admin/src/css/_base.css +5 -1
  79. django_smartbase_admin/static/sb_admin/src/css/_colors.css +257 -82
  80. django_smartbase_admin/static/sb_admin/src/css/_components.css +61 -13
  81. django_smartbase_admin/static/sb_admin/src/css/_datepicker.css +8 -1
  82. django_smartbase_admin/static/sb_admin/src/css/_filer.css +60 -0
  83. django_smartbase_admin/static/sb_admin/src/css/_inlines.css +51 -10
  84. django_smartbase_admin/static/sb_admin/src/css/_tabulator.css +8 -2
  85. django_smartbase_admin/static/sb_admin/src/css/calendar.css +162 -0
  86. django_smartbase_admin/static/sb_admin/src/css/components/_button.css +41 -1
  87. django_smartbase_admin/static/sb_admin/src/css/components/_dropdown.css +31 -7
  88. django_smartbase_admin/static/sb_admin/src/css/components/_input.css +62 -20
  89. django_smartbase_admin/static/sb_admin/src/css/components/_modal.css +1 -1
  90. django_smartbase_admin/static/sb_admin/src/css/components/_query-builder.css +21 -2
  91. django_smartbase_admin/static/sb_admin/src/css/components/_toggle.css +12 -1
  92. django_smartbase_admin/static/sb_admin/src/css/components/_tooltip.css +8 -22
  93. django_smartbase_admin/static/sb_admin/src/css/style.css +17 -0
  94. django_smartbase_admin/static/sb_admin/src/css/tree_widget.css +411 -0
  95. django_smartbase_admin/static/sb_admin/src/js/autocomplete.js +69 -11
  96. django_smartbase_admin/static/sb_admin/src/js/calendar.js +56 -0
  97. django_smartbase_admin/static/sb_admin/src/js/chart.js +8 -22
  98. django_smartbase_admin/static/sb_admin/src/js/choices.js +18 -8
  99. django_smartbase_admin/static/sb_admin/src/js/datepicker.js +97 -336
  100. django_smartbase_admin/static/sb_admin/src/js/datepicker_plugins.js +357 -0
  101. django_smartbase_admin/static/sb_admin/src/js/main.js +306 -31
  102. django_smartbase_admin/static/sb_admin/src/js/multiselect.js +50 -41
  103. django_smartbase_admin/static/sb_admin/src/js/radio.js +31 -0
  104. django_smartbase_admin/static/sb_admin/src/js/range.js +3 -2
  105. django_smartbase_admin/static/sb_admin/src/js/table.js +34 -5
  106. django_smartbase_admin/static/sb_admin/src/js/table_modules/advanced_filter_module.js +43 -20
  107. django_smartbase_admin/static/sb_admin/src/js/table_modules/data_edit_module.js +8 -10
  108. django_smartbase_admin/static/sb_admin/src/js/table_modules/detail_view_module.js +50 -1
  109. django_smartbase_admin/static/sb_admin/src/js/table_modules/filter_module.js +10 -3
  110. django_smartbase_admin/static/sb_admin/src/js/table_modules/header_tabs_module.js +11 -11
  111. django_smartbase_admin/static/sb_admin/src/js/table_modules/selection_module.js +28 -8
  112. django_smartbase_admin/static/sb_admin/src/js/table_modules/table_params_module.js +6 -0
  113. django_smartbase_admin/static/sb_admin/src/js/table_modules/views_module.js +6 -0
  114. django_smartbase_admin/static/sb_admin/src/js/tree_widget.js +406 -0
  115. django_smartbase_admin/static/sb_admin/src/js/utils.js +56 -21
  116. django_smartbase_admin/templates/sb_admin/actions/change_form.html +176 -116
  117. django_smartbase_admin/templates/sb_admin/actions/dashboard.html +2 -2
  118. django_smartbase_admin/templates/sb_admin/actions/list.html +79 -39
  119. django_smartbase_admin/templates/sb_admin/actions/partials/action_link.html +14 -0
  120. django_smartbase_admin/templates/sb_admin/actions/partials/tabulator_header_v2.html +2 -2
  121. django_smartbase_admin/templates/sb_admin/actions/tree_list.html +63 -0
  122. django_smartbase_admin/templates/sb_admin/authentification/login_base.html +5 -1
  123. django_smartbase_admin/templates/sb_admin/components/columns.html +1 -1
  124. django_smartbase_admin/templates/sb_admin/components/filters.html +1 -0
  125. django_smartbase_admin/templates/sb_admin/components/filters_v2.html +99 -85
  126. django_smartbase_admin/templates/sb_admin/dashboard/calendar_widget.html +69 -0
  127. django_smartbase_admin/templates/sb_admin/dashboard/chart_widget.html +21 -2
  128. django_smartbase_admin/templates/sb_admin/dashboard/list_widget.html +6 -0
  129. django_smartbase_admin/templates/sb_admin/dashboard/widget_base.html +1 -1
  130. django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/date_field.html +18 -8
  131. django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/multiple_choice_field.html +1 -1
  132. django_smartbase_admin/templates/sb_admin/filter_widgets/advanced_filters/tree_select_filter.html +2 -0
  133. django_smartbase_admin/templates/sb_admin/filter_widgets/boolean_field.html +1 -14
  134. django_smartbase_admin/templates/sb_admin/filter_widgets/date_field.html +18 -4
  135. django_smartbase_admin/templates/sb_admin/filter_widgets/multiple_choice_field.html +14 -0
  136. django_smartbase_admin/templates/sb_admin/filter_widgets/partials/clear.html +12 -6
  137. django_smartbase_admin/templates/sb_admin/filter_widgets/radio_choice_field.html +5 -3
  138. django_smartbase_admin/templates/sb_admin/filter_widgets/tree_select_filter.html +16 -0
  139. django_smartbase_admin/templates/sb_admin/includes/change_form_title.html +3 -1
  140. django_smartbase_admin/templates/sb_admin/includes/inline_fieldset.html +48 -39
  141. django_smartbase_admin/templates/sb_admin/includes/notifications.html +2 -1
  142. django_smartbase_admin/templates/sb_admin/includes/readonly_boolean_field.html +9 -0
  143. django_smartbase_admin/templates/sb_admin/includes/readonly_field.html +12 -0
  144. django_smartbase_admin/templates/sb_admin/includes/table_inline_delete_button.html +4 -5
  145. django_smartbase_admin/templates/sb_admin/inlines/stacked_inline.html +68 -40
  146. django_smartbase_admin/templates/sb_admin/inlines/table_inline.html +76 -34
  147. django_smartbase_admin/templates/sb_admin/integrations/filer/folder_list.html +18 -0
  148. django_smartbase_admin/templates/sb_admin/navigation.html +166 -158
  149. django_smartbase_admin/templates/sb_admin/partials/modal/modal_content.html +2 -6
  150. django_smartbase_admin/templates/sb_admin/sb_admin_base.html +49 -4
  151. django_smartbase_admin/templates/sb_admin/sb_admin_base_no_sidebar.html +27 -11
  152. django_smartbase_admin/templates/sb_admin/sb_admin_js_trans.html +3 -0
  153. django_smartbase_admin/templates/sb_admin/sprites/sb_admin.svg +1 -1
  154. django_smartbase_admin/templates/sb_admin/tailwind_whitelist.html +6 -3
  155. django_smartbase_admin/templates/sb_admin/widgets/array.html +0 -1
  156. django_smartbase_admin/templates/sb_admin/widgets/attributes.html +68 -0
  157. django_smartbase_admin/templates/sb_admin/widgets/autocomplete.html +13 -2
  158. django_smartbase_admin/templates/sb_admin/widgets/{checkbox_select.html → checkbox_dropdown.html} +2 -2
  159. django_smartbase_admin/templates/sb_admin/widgets/clearable_file_input.html +2 -2
  160. django_smartbase_admin/templates/sb_admin/widgets/color_field.html +30 -0
  161. django_smartbase_admin/templates/sb_admin/widgets/date.html +8 -1
  162. django_smartbase_admin/templates/sb_admin/widgets/filer_file.html +84 -0
  163. django_smartbase_admin/templates/sb_admin/widgets/includes/related_item_buttons.html +38 -0
  164. django_smartbase_admin/templates/sb_admin/widgets/multiwidget.html +1 -1
  165. django_smartbase_admin/templates/sb_admin/widgets/radio.html +3 -2
  166. django_smartbase_admin/templates/sb_admin/widgets/radio_dropdown.html +30 -0
  167. django_smartbase_admin/templates/sb_admin/widgets/read_only_password_hash.html +3 -0
  168. django_smartbase_admin/templates/sb_admin/widgets/time.html +8 -1
  169. django_smartbase_admin/templates/sb_admin/widgets/toggle.html +1 -1
  170. django_smartbase_admin/templates/sb_admin/widgets/tree_base.html +59 -0
  171. django_smartbase_admin/templates/sb_admin/widgets/tree_select.html +24 -0
  172. django_smartbase_admin/templates/sb_admin/widgets/tree_select_inline.html +12 -0
  173. django_smartbase_admin/templatetags/sb_admin_tags.py +115 -4
  174. django_smartbase_admin/utils.py +22 -3
  175. django_smartbase_admin/views/dashboard_view.py +6 -0
  176. django_smartbase_admin/views/global_filter_view.py +8 -2
  177. django_smartbase_admin/views/translations_view.py +12 -5
  178. django_smartbase_admin/views/user_config_view.py +52 -0
  179. django_smartbase_admin-1.0.42.dist-info/METADATA +166 -0
  180. {django_smartbase_admin-0.2.54.dist-info → django_smartbase_admin-1.0.42.dist-info}/RECORD +182 -118
  181. {django_smartbase_admin-0.2.54.dist-info → django_smartbase_admin-1.0.42.dist-info}/WHEEL +1 -1
  182. django_smartbase_admin/templates/sb_admin/integrations/sorting/change_list.html +0 -401
  183. django_smartbase_admin-0.2.54.dist-info/METADATA +0 -25
  184. {django_smartbase_admin-0.2.54.dist-info → django_smartbase_admin-1.0.42.dist-info}/LICENSE.md +0 -0
@@ -3,9 +3,16 @@ import Collapse from 'bootstrap/js/dist/collapse'
3
3
  import Tab from 'bootstrap/js/dist/tab'
4
4
  import Modal from 'bootstrap/js/dist/modal'
5
5
  import Tooltip from 'bootstrap/js/dist/tooltip'
6
+ import debounce from 'lodash/debounce'
6
7
 
7
8
  // remove Modal focus trap to fix interaction with fields in modals inside another modal
8
- Modal.prototype._initializeFocusTrap = function () { return { activate: function () { }, deactivate: function () { } } }
9
+ Modal.prototype._initializeFocusTrap = function () {
10
+ return {
11
+ activate: function () {
12
+ }, deactivate: function () {
13
+ }
14
+ }
15
+ }
9
16
 
10
17
  window.bootstrap5 = {
11
18
  Modal: Modal,
@@ -21,17 +28,19 @@ import Range from "./range"
21
28
  import Sorting from "./sorting"
22
29
  import Autocomplete from "./autocomplete"
23
30
  import ChoicesJS from "./choices"
24
- import {setCookie} from "./utils"
31
+ import {setCookie, setDropdownLabel} from "./utils"
25
32
  import Multiselect from "./multiselect"
33
+ import Radio from "./radio"
26
34
 
27
35
  class Main {
28
36
  constructor() {
29
37
  document.body.classList.add('js-ready')
38
+ this.handleColorSchemeChange()
30
39
 
31
40
  const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
32
41
  tooltipTriggerList.map((tooltipTriggerEl) => {
33
42
  const tooltipEl = tooltipTriggerEl.closest('.js-tooltip')
34
- if(tooltipEl) {
43
+ if (tooltipEl) {
35
44
  return new Tooltip(tooltipTriggerEl, {container: tooltipEl})
36
45
  }
37
46
  return null
@@ -41,35 +50,124 @@ class Main {
41
50
  document.addEventListener('formset:added', (e) => {
42
51
  this.initDropdowns(e.target)
43
52
  this.initFileInputs(e.target)
53
+ this.switchCKEditorTheme(e.target)
44
54
  if (e.target !== e.target.parentNode.firstChild) {
45
55
  e.target.parentNode.insertBefore(e.target, e.target.parentNode.firstChild)
46
56
  }
57
+ window.htmx.process(e.target)
47
58
  })
48
59
  document.addEventListener('openUrl', (e) => {
49
60
  window.open(e.detail.url, e.detail?.target || '_blank')
50
61
  })
51
62
 
63
+ if (window.htmx) {
64
+ const shouldProcessAfterSwap = (detail) => {
65
+ const requestEl = detail.detail.requestConfig.elt.closest('[hx-swap]')
66
+ if (requestEl && requestEl.getAttribute('hx-swap') === "none") {
67
+ // do not process afterSwap if none swap is performed
68
+ // this should prevent double processing of afterSwap for first oob-swapped element
69
+ // which in case of hx-swap=none is returned here in the detail.target
70
+ return false
71
+ }
72
+ return true
73
+ }
74
+
75
+ window.htmx.on("htmx:afterSwap", (detail) => {
76
+ if(!shouldProcessAfterSwap(detail)) {
77
+ return
78
+ }
79
+ this.initFileInputs(detail.target)
80
+ this.initDropdowns(detail.target)
81
+ this.initInputs(detail.target)
82
+ this.autocomplete.handleDynamiclyAddedAutocomplete(detail.target)
83
+ this.initInlines(detail.target)
84
+ })
85
+
86
+ window.htmx.on("htmx:afterSettle", (detail) => {
87
+ if(!shouldProcessAfterSwap(detail)) {
88
+ return
89
+ }
90
+ this.switchCKEditorTheme(detail.target)
91
+ })
92
+ }
93
+
52
94
  new Sidebar()
53
- this.datepicker = new Datepicker()
54
- this.range = new Range()
95
+ this.initInputs()
55
96
  new Sorting()
56
97
  this.autocomplete = new Autocomplete()
57
98
  new ChoicesJS()
58
- this.multiselect = new Multiselect()
59
99
  document.addEventListener('click', (e) => {
60
100
  this.closeAlert(e)
61
101
  this.selectAll(e)
62
102
  this.saveState(e)
63
103
  this.fileDownload(e)
64
104
  this.passwordToggleFnc(e)
105
+ this.collapseStackedInlineButtons(e)
65
106
  })
66
107
  this.initFileInputs()
67
108
  this.initAliasName()
68
109
  this.handleLocationHashFromTabs()
110
+ this.initCollapseEventListeners()
111
+ }
112
+
113
+ isDarkMode() {
114
+ const colorScheme = document.documentElement.dataset.theme
115
+ let isDark = colorScheme === 'dark'
116
+ if(!colorScheme || colorScheme === 'auto') {
117
+ isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
118
+ }
119
+ return isDark
120
+ }
121
+
122
+ handleColorSchemeChange() {
123
+ const picker = document.querySelector('.js-color-scheme-picker')
124
+ if(!picker) {
125
+ return
126
+ }
127
+ picker.addEventListener('change', (e)=>{
128
+ if(e.target.value) {
129
+ document.documentElement.setAttribute('data-theme', e.target.value)
130
+ this.switchBodyColorSchemeClass(true)
131
+ this.switchCKEditorTheme()
132
+ return
133
+ }
134
+ document.documentElement.removeAttribute('data-theme')
135
+ })
136
+ this.switchBodyColorSchemeClass()
137
+ this.switchCKEditorTheme()
138
+ }
139
+
140
+ switchBodyColorSchemeClass(fireEvents = false) {
141
+ if(this.isDarkMode()) {
142
+ document.body.classList.add('dark')
143
+ if(fireEvents) {
144
+ document.body.dispatchEvent(new CustomEvent('color-scheme-change', {detail: 'dark'}))
145
+ }
146
+ return
147
+ }
148
+ document.body.classList.remove('dark')
149
+ if(fireEvents) {
150
+ document.body.dispatchEvent(new CustomEvent('color-scheme-change', {detail: 'light'}))
151
+ }
152
+ }
153
+
154
+ initInlines(target) {
155
+ target = target || document
156
+ const inlineGroups = target.querySelectorAll('.inline-group')
157
+ inlineGroups.forEach(group => {
158
+ window.django.jQuery(group).djangoFormset()
159
+ })
160
+ }
161
+
162
+ initInputs(target) {
163
+ this.datepicker = new Datepicker(target)
164
+ this.range = new Range(null, null, target)
165
+ this.multiselect = new Multiselect(null, null, target)
166
+ this.radio = new Radio(null, target)
69
167
  }
70
168
 
71
169
  handleLocationHashFromTabs() {
72
- if(window.location.hash) {
170
+ if (window.location.hash) {
73
171
  document.querySelector(`#tab_${window.location.hash.slice(1)}`)?.click()
74
172
  }
75
173
  const tabEls = document.querySelectorAll('button[data-bs-toggle="tab"]:not([data-bs-disable-history])')
@@ -102,7 +200,7 @@ class Main {
102
200
 
103
201
  fileDownload(event) {
104
202
  const button = event.target.closest('.js-file-button')
105
- if(button) {
203
+ if (button) {
106
204
  event.preventDefault()
107
205
  event.stopPropagation()
108
206
  const download_window = window.open(button.getAttribute("href"))
@@ -118,34 +216,45 @@ class Main {
118
216
  const dropdowns = [].slice.call(target.querySelectorAll('[data-bs-toggle="dropdown"]'))
119
217
  dropdowns.map((dropdownToggleEl) => {
120
218
  let offset = dropdownToggleEl.dataset['bsOffset']
121
- if(offset) {
219
+ if (offset) {
122
220
  offset = JSON.parse(dropdownToggleEl.dataset['bsOffset'])
221
+ } else {
222
+ offset = [0, 8]
123
223
  }
124
- else {
125
- offset = [0,8]
126
- }
127
- return new Dropdown(dropdownToggleEl, {
224
+ const dropdown = new Dropdown(dropdownToggleEl, {
128
225
  autoClose: 'outside',
129
226
  offset: offset,
130
227
  popperConfig(defaultBsPopperConfig) {
131
228
  const elementConf = {}
132
- if(dropdownToggleEl.dataset['bsPopperPlacement']) {
229
+ if (dropdownToggleEl.dataset['bsPopperPlacement']) {
133
230
  elementConf['placement'] = dropdownToggleEl.dataset['bsPopperPlacement']
134
231
  }
135
- return { ...defaultBsPopperConfig, ...elementConf, strategy: 'fixed' }
232
+ return {...defaultBsPopperConfig, ...elementConf, strategy: 'fixed'}
136
233
  }
137
234
  })
235
+ const dropdownWrapper = dropdownToggleEl.closest('.js-dropdown-wrapper')
236
+ if(dropdownWrapper) {
237
+ const dropdownLabelEl = dropdownWrapper.querySelector('.js-dropdown-label')
238
+ dropdown._menu.addEventListener('change', ()=>{
239
+ setDropdownLabel(dropdown._menu, dropdownLabelEl)
240
+ })
241
+ }
242
+ return dropdown
138
243
  })
139
244
  }
140
245
 
141
246
  initAliasName() {
247
+ if(!window.sb_admin_const) {
248
+ return
249
+ }
142
250
  const aliasGroup = document.getElementById(window.sb_admin_const.GLOBAL_FILTER_ALIAS_WIDGET_ID)
143
- if(!aliasGroup) {
251
+ if (!aliasGroup) {
144
252
  return
145
253
  }
146
254
 
147
255
  const changeAliasName = () => {
148
- const currentAlias = aliasGroup.querySelector('input[name="alias"]:checked')
256
+ const currentAlias = aliasGroup.querySelector('input.js-alias-domain-name-value:checked') ||
257
+ aliasGroup.querySelector('input[name="alias"]:checked')
149
258
  if (!currentAlias) {
150
259
  return
151
260
  }
@@ -165,9 +274,9 @@ class Main {
165
274
  const saveStateEl = event.target.closest('.js-save-state')
166
275
  if (saveStateEl) {
167
276
  const isBsToggle = saveStateEl.dataset['bsToggle']
168
- if(isBsToggle === 'collapse') {
277
+ if (isBsToggle === 'collapse') {
169
278
  const expanded = saveStateEl.getAttribute('aria-expanded') === 'true'
170
- setCookie(saveStateEl.id, expanded, expanded?1:0)
279
+ setCookie(saveStateEl.id, expanded, expanded ? 1 : 0)
171
280
  }
172
281
  }
173
282
  }
@@ -181,7 +290,7 @@ class Main {
181
290
  selectAll(event) {
182
291
  const wrapper = event.target.closest('.js-select-all-wrapper')
183
292
 
184
- if(wrapper) {
293
+ if (wrapper) {
185
294
  const selectAll = event.target.closest('.js-select-all')
186
295
  const clearAll = event.target.closest('.js-clear-all')
187
296
  if (selectAll) {
@@ -216,17 +325,28 @@ class Main {
216
325
  const input = fileInput.querySelector('input[type="file"]')
217
326
  const delete_checkbox = fileInput.querySelector('input[type="checkbox"]')
218
327
  input?.addEventListener('change', e => {
219
- if(delete_checkbox) {
328
+ if (delete_checkbox) {
220
329
  delete_checkbox.checked = false
221
330
  }
222
- if(e.target.files[0]) {
331
+ if (e.target.files[0]) {
223
332
  fileInput.classList.add('filled')
224
333
  fileInput.querySelectorAll('.js-input-file-image').forEach(el => {
225
- el.src = URL.createObjectURL(e.target.files[0])
334
+ const nameSplit= e.target.files[0].name.split('.')
335
+ const extension = nameSplit[nameSplit.length - 1]
336
+ if(['jpg', 'jpeg', 'png', 'svg', 'webp'].includes(extension)) {
337
+ el.src = URL.createObjectURL(e.target.files[0])
338
+ el.classList.add('border')
339
+ return
340
+ }
341
+ el.classList.remove('border')
342
+ if(window.sb_admin_const.SUPPORTED_FILE_TYPE_ICONS.includes(extension)) {
343
+ el.src = `${window.sb_admin_const.STATIC_BASE_PATH}/images/file_types/file-${extension}.svg`
344
+ return
345
+ }
346
+ el.src = `${window.sb_admin_const.STATIC_BASE_PATH}/images/file_types/file-other.svg`
226
347
  })
227
348
  fileInput.querySelector('.js-input-file-filename').innerHTML = e.target.files[0].name
228
- }
229
- else {
349
+ } else {
230
350
  fileInput.classList.remove('filled')
231
351
  fileInput.querySelector('.js-input-file-filename').innerHTML = ""
232
352
  }
@@ -236,13 +356,51 @@ class Main {
236
356
  deleteButton?.addEventListener('click', () => {
237
357
  input.value = ""
238
358
  input.dispatchEvent(new Event('change'))
239
- if(delete_checkbox) {
359
+ if (delete_checkbox) {
240
360
  delete_checkbox.checked = true
241
361
  }
242
362
  })
243
363
  })
244
364
  }
245
365
 
366
+ initCKEditor(target, config, force=false) {
367
+ if (!window.CKEDITOR) {
368
+ return
369
+ }
370
+ target = target || document
371
+ target.querySelectorAll('textarea[data-type="ckeditortype"]').forEach((textarea) => {
372
+ if( force || textarea.getAttribute("data-processed") == "0") {
373
+ if(textarea.id.indexOf("__prefix__") == -1) {
374
+ this.reinitCKEditor(textarea, config)
375
+ }
376
+ }
377
+ })
378
+ }
379
+
380
+ reinitCKEditor(textarea, config) {
381
+ const id = textarea.id
382
+ if (!id) {
383
+ return
384
+ }
385
+ if(window.CKEDITOR.instances[id]) {
386
+ window.CKEDITOR.instances[id].destroy(true)
387
+ }
388
+ config = config || {}
389
+ const new_config = {...JSON.parse(textarea.getAttribute("data-config")), ...config}
390
+ window.CKEDITOR.replace(id, new_config)
391
+ }
392
+
393
+ switchCKEditorTheme(target) {
394
+ if(!window.CKEDITOR) {
395
+ return
396
+ }
397
+ if(this.isDarkMode()) {
398
+ this.initCKEditor(target, {'contentsCss': '/static/sb_admin/css/ckeditor/ckeditor_content_dark.css', uiColor: '#000000'}, true)
399
+ return
400
+ }
401
+ this.initCKEditor(target, {'contentsCss':window.CKEDITOR.config.contentsCss}, true)
402
+ }
403
+
246
404
  clearFilter(inputId) {
247
405
  const fieldElem = document.querySelector(`#${inputId}`)
248
406
  fieldElem.value = ''
@@ -250,11 +408,119 @@ class Main {
250
408
  fieldElem.dispatchEvent(new CustomEvent('clear', {detail: {refresh: true}}))
251
409
  }
252
410
 
253
- executeListAction(table_id, action_url, no_params) {
254
- if(window.SBAdminTable && window.SBAdminTable[table_id]){
255
- window.SBAdminTable[table_id].executeListAction(action_url, no_params)
256
- } else{
257
- window.location.href = action_url
411
+
412
+
413
+ executeListAction(table_id, action_url, no_params, open_in_new_tab = false) {
414
+ if (window.SBAdminTable && window.SBAdminTable[table_id]) {
415
+ window.SBAdminTable[table_id].executeListAction(action_url, no_params, open_in_new_tab)
416
+ } else {
417
+ if (open_in_new_tab) {
418
+ window.open(action_url, '_blank')
419
+ } else {
420
+ window.location.href = action_url
421
+ }
422
+ }
423
+ }
424
+ isCurrentlyCollapsed(parentWrapper) {
425
+ const collapseElements = parentWrapper.querySelectorAll('.collapse')
426
+ return Array.from(collapseElements).every(el => {
427
+ if(el.closest('.djn-empty-form')) {
428
+ return true
429
+ }
430
+ return !el.classList.contains('show')
431
+ })
432
+ }
433
+
434
+ updateCollapseAllButton(parentWrapper) {
435
+ const collapseAll = parentWrapper.querySelector('.collapse-all-stacked-inlines')
436
+ if (!collapseAll) return
437
+
438
+ const isCollapsed = this.isCurrentlyCollapsed(parentWrapper)
439
+ const expandText = `<svg class="mr-8"><use xlink:href="#View-grid-list"></use></svg><span>${window.sb_admin_translation_strings["expand"]}</span>`
440
+ const collapseText = `<svg class="mr-8"><use xlink:href="#List-checkbox"></use></svg><span>${window.sb_admin_translation_strings["collapse"]}</span>`
441
+
442
+ if (isCollapsed) {
443
+ collapseAll.classList.add('collapsed')
444
+ collapseAll.innerHTML = expandText
445
+ } else {
446
+ collapseAll.classList.remove('collapsed')
447
+ collapseAll.innerHTML = collapseText
448
+ }
449
+ }
450
+
451
+ initCollapseEventListeners() {
452
+ this.initCollapseAllButtons()
453
+
454
+ const debouncedUpdateCollapseAllButton = debounce((parentWrapper) => {
455
+ this.updateCollapseAllButton(parentWrapper)
456
+ }, 50)
457
+
458
+ document.addEventListener('shown.bs.collapse', (e) => {
459
+ const parentWrapper = e.target.closest('.djn-fieldset')
460
+ if (parentWrapper) {
461
+ debouncedUpdateCollapseAllButton(parentWrapper)
462
+ }
463
+ })
464
+
465
+ document.addEventListener('hidden.bs.collapse', (e) => {
466
+ const parentWrapper = e.target.closest('.djn-fieldset')
467
+ if (parentWrapper) {
468
+ debouncedUpdateCollapseAllButton(parentWrapper)
469
+ }
470
+ })
471
+ }
472
+
473
+
474
+
475
+ initCollapseAllButtons() {
476
+ const collapseAllButtons = document.querySelectorAll('.collapse-all-stacked-inlines')
477
+ collapseAllButtons.forEach(button => {
478
+ const parentWrapper = button.closest('.djn-fieldset')
479
+ if (parentWrapper) {
480
+ this.updateCollapseAllButton(parentWrapper)
481
+ }
482
+ })
483
+ }
484
+
485
+ collapseStackedInlineButtons(event) {
486
+ const collapseStackedInline = event.target.closest('.js-collapse-stacked-inline')
487
+ if(collapseStackedInline) {
488
+ const collapseEl = event.target.closest('.djn-inline-form').querySelector('.collapse')
489
+ const instance = Collapse.getOrCreateInstance(collapseEl)
490
+ instance.toggle()
491
+ collapseStackedInline.setAttribute('aria-expanded', collapseStackedInline.getAttribute('aria-expanded') !== 'true')
492
+ }
493
+
494
+ const collapseAll = event.target.closest('.collapse-all-stacked-inlines')
495
+ if (collapseAll) {
496
+ event.preventDefault()
497
+ const parentWrapper = collapseAll.closest('.djn-fieldset')
498
+ const collapseElements = parentWrapper.querySelectorAll('.collapse')
499
+ const collapseTriggers = parentWrapper.querySelectorAll('.js-collapse-stacked-inline')
500
+ const isCurrentlyCollapsed = this.isCurrentlyCollapsed(parentWrapper)
501
+
502
+ collapseTriggers.forEach(el => {
503
+ if(el.closest('.djn-empty-form')) {
504
+ return
505
+ }
506
+ if (isCurrentlyCollapsed) {
507
+ el.setAttribute('aria-expanded', 'true')
508
+ } else {
509
+ el.setAttribute('aria-expanded', 'false')
510
+ }
511
+ })
512
+
513
+ collapseElements.forEach(el => {
514
+ if(el.closest('.djn-empty-form')) {
515
+ return
516
+ }
517
+ const instance = Collapse.getOrCreateInstance(el)
518
+ if (isCurrentlyCollapsed) {
519
+ instance.show()
520
+ } else {
521
+ instance.hide()
522
+ }
523
+ })
258
524
  }
259
525
  }
260
526
  }
@@ -262,3 +528,12 @@ class Main {
262
528
  window.addEventListener('DOMContentLoaded', () => {
263
529
  window.SBAdmin = new Main()
264
530
  })
531
+
532
+ document.body.addEventListener('sbadmin:modal-change-form-response', function (event) {
533
+ if (event.detail.reload) {
534
+ window.location.reload()
535
+ }
536
+ if (event.detail.loadUrl) {
537
+ window.location.href = event.detail.loadUrl
538
+ }
539
+ })
@@ -1,37 +1,61 @@
1
+ import {setDropdownLabel} from "./utils"
2
+
1
3
  export default class Multiselect {
2
- constructor(selector_override, options_override) {
4
+ constructor(selector_override, options_override, target) {
5
+ target = target || document
3
6
  const selector = selector_override || '.js-simple-multiselect'
4
7
  const selectorDetail = '.js-simple-multiselect-detail'
8
+ const selectAllClass = 'js-simple-multiselect-all'
5
9
  this.wrapperSelector = '.js-simple-multiselect-wrapper'
6
- document.addEventListener('change', e => {
7
- const wrapperEl = e.target.closest(this.wrapperSelector)
8
- const multiselectInput = wrapperEl?.querySelector(selector)
9
- const isCheckboxClicked = e.target.type === 'checkbox'
10
- if (multiselectInput && isCheckboxClicked) {
11
- let checked = []
12
- this.getCheckboxes(multiselectInput).forEach(el => {
13
- if (el.checked) {
14
- checked.push({
15
- value: el.value,
16
- label: el.dataset.label,
10
+ if(target === document) {
11
+ document.addEventListener('change', e => {
12
+ const wrapperEl = e.target.closest(this.wrapperSelector)
13
+ const multiselectInput = wrapperEl?.querySelector(selector)
14
+ if(!wrapperEl || !multiselectInput) {
15
+ // not filter widget
16
+ return
17
+ }
18
+ const isCheckboxClicked = e.target.type === 'checkbox'
19
+ const selectAllEl = wrapperEl?.querySelector(`.${selectAllClass}`)
20
+ if(isCheckboxClicked) {
21
+ const checkboxes = Array.from(this.getCheckboxes(multiselectInput))
22
+ if(e.target.classList.contains(selectAllClass)) {
23
+ checkboxes.forEach(el => {
24
+ el.checked = e.target.checked
17
25
  })
26
+ } else {
27
+ if(selectAllEl) {
28
+ selectAllEl.checked = false
29
+ }
30
+ }
31
+ if(selectAllEl && !checkboxes.some(el => el.checked)) {
32
+ selectAllEl.checked = true
18
33
  }
34
+ let checked = []
35
+ checkboxes.forEach(el => {
36
+ if(el.checked) {
37
+ checked.push({
38
+ value: el.value,
39
+ label: el.dataset.label,
40
+ })
41
+ }
42
+ })
43
+ multiselectInput.value = JSON.stringify(checked)
44
+ multiselectInput.dispatchEvent(new Event('change', {bubbles: true}))
45
+ }
46
+ })
47
+ document.addEventListener('formset:added', (event) => {
48
+ event.target.querySelectorAll(selectorDetail).forEach(el => {
49
+ this.initDetailMultiselect(el)
19
50
  })
20
- multiselectInput.value = JSON.stringify(checked)
21
- multiselectInput.dispatchEvent(new Event('change', {bubbles: true}))
22
- }
23
- })
24
- document.querySelectorAll(selector).forEach(el => {
51
+ })
52
+ }
53
+ target.querySelectorAll(selector).forEach(el => {
25
54
  this.initMultiselect(el, options_override)
26
55
  })
27
- document.querySelectorAll(selectorDetail).forEach(el => {
56
+ target.querySelectorAll(selectorDetail).forEach(el => {
28
57
  this.initDetailMultiselect(el)
29
58
  })
30
- document.addEventListener('formset:added', (event) => {
31
- event.target.querySelectorAll(selectorDetail).forEach(el => {
32
- this.initDetailMultiselect(el)
33
- })
34
- })
35
59
  }
36
60
 
37
61
  getCheckboxes(base_input) {
@@ -61,35 +85,20 @@ export default class Multiselect {
61
85
  })
62
86
  }
63
87
 
64
-
65
- setLabel(wrapper, valueEl) {
66
- let labels = []
67
- wrapper.querySelectorAll('input[type="checkbox"]').forEach(el => {
68
- if (el.checked) {
69
- labels.push(document.querySelector(`label[for="${el.id}"]`).innerText)
70
- }
71
- })
72
- valueEl.innerHTML = labels.join(',')
73
- }
74
-
75
88
  clearAll(wrapper, valueEl) {
76
89
  wrapper.querySelectorAll('input[type="checkbox"]').forEach(el => {
77
90
  el.checked = false
78
91
  })
79
- this.setLabel(wrapper, valueEl)
92
+ setDropdownLabel(wrapper, valueEl)
80
93
  }
81
94
 
82
95
 
83
96
  initDetailMultiselect(wrapper) {
84
- const valueEl = wrapper.querySelector('.js-value')
85
97
  const clearEl = wrapper.querySelector('.js-clear')
86
98
 
87
- wrapper.addEventListener('change', () => {
88
- this.setLabel(wrapper, valueEl)
89
- })
90
99
  clearEl?.addEventListener('click', () => {
91
- this.clearAll(wrapper, valueEl)
100
+ this.clearAll(wrapper)
92
101
  })
93
- this.setLabel(wrapper, valueEl)
102
+ setDropdownLabel(wrapper)
94
103
  }
95
104
  }
@@ -0,0 +1,31 @@
1
+ export default class Radio {
2
+ constructor(selector_override, target) {
3
+ target = target || document
4
+ const selector = selector_override || '.js-radio-choice-widget'
5
+ this.wrapperSelector = '.js-radio-choice-widget-wrapper'
6
+
7
+ target.querySelectorAll(selector).forEach(el => {
8
+ this.initRadio(el)
9
+ })
10
+ }
11
+
12
+ getRadios(base_input) {
13
+ return base_input.closest(this.wrapperSelector)?.querySelectorAll("input[type='radio']")
14
+ }
15
+
16
+ initRadio(base_input) {
17
+ base_input.addEventListener('SBTableFilterFormLoad', () => {
18
+ if (!base_input.value) {
19
+ return
20
+ }
21
+ this.getRadios(base_input).forEach(el => {
22
+ if(el.value === base_input.value) {
23
+ el.checked = true
24
+ }
25
+ })
26
+ })
27
+ base_input.addEventListener('clear', () => {
28
+ base_input.closest(this.wrapperSelector)?.querySelectorAll("input[type='radio']").forEach(el => el.checked = false)
29
+ })
30
+ }
31
+ }
@@ -1,9 +1,10 @@
1
1
 
2
2
  export default class Range {
3
- constructor(selector_override, options_override) {
3
+ constructor(selector_override, options_override, target) {
4
+ target = target || document
4
5
  const selector = selector_override || '.js-range'
5
6
  this.separator = ' - '
6
- document.querySelectorAll(selector).forEach(el => {
7
+ target.querySelectorAll(selector).forEach(el => {
7
8
  this.initRange(el, options_override)
8
9
  })
9
10
  }