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