headmin 0.2.7 → 0.3.3

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +29 -3
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +39 -24
  6. data/README.md +64 -57
  7. data/app/assets/config/headmin_manifest.js +2 -0
  8. data/app/assets/images/document.docx +0 -0
  9. data/{src/scss/headmin/filters.scss → app/assets/images/document.pdf} +0 -0
  10. data/app/assets/images/image.jpg +0 -0
  11. data/app/assets/images/spreadsheet.xls +0 -0
  12. data/app/assets/images/video.mp4 +0 -0
  13. data/app/assets/javascripts/headmin/config/i18n.js +11 -0
  14. data/{src/js → app/assets/javascripts}/headmin/controllers/blocks_controller.js +0 -1
  15. data/app/assets/javascripts/headmin/controllers/date_range_controller.js +32 -0
  16. data/app/assets/javascripts/headmin/controllers/dropzone_controller.js +33 -0
  17. data/app/assets/javascripts/headmin/controllers/file_preview_controller.js +244 -0
  18. data/{src/js → app/assets/javascripts}/headmin/controllers/filter_controller.js +12 -6
  19. data/{src/js → app/assets/javascripts}/headmin/controllers/filters_controller.js +0 -0
  20. data/app/assets/javascripts/headmin/controllers/flatpickr_controller.js +39 -0
  21. data/app/assets/javascripts/headmin/controllers/hello_controller.js +7 -0
  22. data/app/assets/javascripts/headmin/controllers/notification_controller.js +8 -0
  23. data/{src/js → app/assets/javascripts}/headmin/controllers/popup_controller.js +0 -1
  24. data/app/assets/javascripts/headmin/controllers/redactorx_controller.js +13 -0
  25. data/{src/js → app/assets/javascripts}/headmin/controllers/repeater_controller.js +0 -1
  26. data/app/assets/javascripts/headmin/controllers/select_controller.js +48 -0
  27. data/{src/js → app/assets/javascripts}/headmin/controllers/table_actions_controller.js +9 -39
  28. data/{src/js → app/assets/javascripts}/headmin/controllers/table_controller.js +51 -16
  29. data/app/assets/javascripts/headmin/index.js +37 -0
  30. data/app/assets/javascripts/headmin.js +15271 -0
  31. data/{src/scss → app/assets/stylesheets}/headmin/filter.scss +0 -0
  32. data/app/assets/stylesheets/headmin/filters.scss +0 -0
  33. data/{src/scss → app/assets/stylesheets}/headmin/form.scss +57 -4
  34. data/{src/scss → app/assets/stylesheets}/headmin/general.scss +0 -0
  35. data/{src/scss → app/assets/stylesheets}/headmin/layout/body.scss +0 -0
  36. data/{src/scss → app/assets/stylesheets}/headmin/layout/sidebar.scss +0 -0
  37. data/{src/scss → app/assets/stylesheets}/headmin/layout.scss +0 -0
  38. data/{src/scss → app/assets/stylesheets}/headmin/login.scss +0 -0
  39. data/{src/scss/vendor/bootstrap/variables.scss → app/assets/stylesheets/headmin/overrides/bootstrap.scss} +0 -0
  40. data/{src/scss/vendor/redactorx/override.css → app/assets/stylesheets/headmin/overrides/redactorx.css} +0 -0
  41. data/{src/scss → app/assets/stylesheets}/headmin/popup.scss +0 -0
  42. data/app/assets/stylesheets/headmin/syntax.scss +349 -0
  43. data/{src/scss → app/assets/stylesheets}/headmin/table.scss +0 -0
  44. data/app/assets/stylesheets/headmin/thumbnail.scss +20 -0
  45. data/app/assets/stylesheets/headmin/utilities.scss +68 -0
  46. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_accordion.scss +118 -0
  47. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_alert.scss +57 -0
  48. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_badge.scss +29 -0
  49. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_breadcrumb.scss +28 -0
  50. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_button-group.scss +139 -0
  51. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_buttons.scss +111 -0
  52. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_card.scss +216 -0
  53. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_carousel.scss +229 -0
  54. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_close.scss +40 -0
  55. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_containers.scss +41 -0
  56. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_dropdown.scss +240 -0
  57. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_forms.scss +9 -0
  58. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_functions.scss +302 -0
  59. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_grid.scss +33 -0
  60. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_helpers.scss +9 -0
  61. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_images.scss +42 -0
  62. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_list-group.scss +174 -0
  63. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_mixins.scss +43 -0
  64. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_modal.scss +209 -0
  65. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_nav.scss +139 -0
  66. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_navbar.scss +335 -0
  67. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_offcanvas.scss +83 -0
  68. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_pagination.scss +64 -0
  69. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_placeholders.scss +51 -0
  70. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_popover.scss +158 -0
  71. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_progress.scss +48 -0
  72. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_reboot.scss +625 -0
  73. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_root.scss +54 -0
  74. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_spinners.scss +69 -0
  75. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_tables.scss +155 -0
  76. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_toasts.scss +51 -0
  77. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_tooltip.scss +115 -0
  78. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_transitions.scss +27 -0
  79. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_type.scss +104 -0
  80. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_utilities.scss +630 -0
  81. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/_variables.scss +1641 -0
  82. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/bootstrap-grid.scss +67 -0
  83. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/bootstrap-reboot.scss +13 -0
  84. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/bootstrap-utilities.scss +18 -0
  85. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/bootstrap.scss +53 -0
  86. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/forms/_floating-labels.scss +63 -0
  87. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/forms/_form-check.scss +152 -0
  88. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/forms/_form-control.scss +219 -0
  89. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/forms/_form-range.scss +91 -0
  90. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/forms/_form-select.scss +72 -0
  91. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/forms/_form-text.scss +11 -0
  92. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/forms/_input-group.scss +121 -0
  93. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/forms/_labels.scss +36 -0
  94. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/forms/_validation.scss +12 -0
  95. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/helpers/_clearfix.scss +3 -0
  96. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/helpers/_colored-links.scss +12 -0
  97. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/helpers/_position.scss +30 -0
  98. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/helpers/_ratio.scss +26 -0
  99. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/helpers/_stacks.scss +15 -0
  100. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/helpers/_stretched-link.scss +15 -0
  101. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/helpers/_text-truncation.scss +7 -0
  102. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/helpers/_visually-hidden.scss +8 -0
  103. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/helpers/_vr.scss +8 -0
  104. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_alert.scss +11 -0
  105. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_backdrop.scss +14 -0
  106. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_border-radius.scss +78 -0
  107. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_box-shadow.scss +18 -0
  108. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_breakpoints.scss +127 -0
  109. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_buttons.scss +133 -0
  110. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_caret.scss +64 -0
  111. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_clearfix.scss +9 -0
  112. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_color-scheme.scss +7 -0
  113. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_container.scss +9 -0
  114. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_deprecate.scss +10 -0
  115. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_forms.scss +144 -0
  116. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_gradients.scss +47 -0
  117. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_grid.scss +151 -0
  118. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_image.scss +16 -0
  119. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_list-group.scss +24 -0
  120. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_lists.scss +7 -0
  121. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_pagination.scss +31 -0
  122. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_reset-text.scss +17 -0
  123. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_resize.scss +6 -0
  124. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_table-variants.scss +21 -0
  125. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_text-truncate.scss +8 -0
  126. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_transition.scss +26 -0
  127. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_utilities.scss +89 -0
  128. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/mixins/_visually-hidden.scss +29 -0
  129. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/utilities/_api.scss +47 -0
  130. data/app/assets/stylesheets/headmin/vendor/bootstrap/scss/vendor/_rfs.scss +354 -0
  131. data/app/assets/stylesheets/headmin/vendor/flatpickr.css +903 -0
  132. data/{src/scss/vendor/redactorx → app/assets/stylesheets/headmin/vendor}/redactorx.css +0 -0
  133. data/app/assets/stylesheets/headmin/vendor/tom-select-bootstrap.css +536 -0
  134. data/app/assets/stylesheets/headmin.css +13454 -0
  135. data/app/assets/stylesheets/headmin.scss +65 -0
  136. data/app/controllers/concerns/headmin/authentication.rb +1 -1
  137. data/app/controllers/concerns/headmin/searchable.rb +1 -1
  138. data/app/controllers/concerns/headmin/sortable.rb +7 -7
  139. data/app/helpers/headmin/admin_helper.rb +2 -1
  140. data/app/helpers/headmin/bootstrap_helper.rb +4 -4
  141. data/app/helpers/headmin/documentation_helper.rb +35 -0
  142. data/app/helpers/headmin/filter_helper.rb +1 -1
  143. data/app/helpers/headmin/form_helper.rb +7 -3
  144. data/app/helpers/headmin/notification_helper.rb +21 -21
  145. data/app/helpers/headmin/request_helper.rb +5 -10
  146. data/app/models/concerns/headmin/block.rb +1 -2
  147. data/app/models/concerns/headmin/blockable.rb +1 -1
  148. data/app/models/concerns/headmin/field.rb +1 -1
  149. data/app/models/concerns/headmin/fieldable.rb +8 -8
  150. data/app/models/headmin/documentation_renderer.rb +32 -0
  151. data/app/models/headmin/form/base.rb +78 -0
  152. data/app/models/headmin/form/text.rb +51 -0
  153. data/app/models/headmin/thumbnail.rb +61 -0
  154. data/app/services/block_service.rb +53 -53
  155. data/app/views/examples/admin.html.erb +1 -1
  156. data/app/views/headmin/_blocks.html.erb +2 -2
  157. data/app/views/headmin/_breadcrumbs.html.erb +1 -1
  158. data/app/views/headmin/_card.html.erb +10 -6
  159. data/app/views/headmin/_dropdown.html.erb +1 -1
  160. data/app/views/headmin/_filters.html.erb +22 -10
  161. data/app/views/headmin/_form.html.erb +14 -14
  162. data/app/views/headmin/_heading.html.erb +1 -1
  163. data/app/views/headmin/_notifications.html.erb +1 -1
  164. data/app/views/headmin/_pagination.html.erb +3 -5
  165. data/app/views/headmin/_popup.html.erb +10 -4
  166. data/app/views/headmin/_table.html.erb +10 -4
  167. data/app/views/headmin/_thumbnail.html.erb +47 -0
  168. data/app/views/headmin/dropdown/_devise.html.erb +2 -2
  169. data/app/views/headmin/dropdown/_item.html.erb +1 -1
  170. data/app/views/headmin/dropdown/_list.html.erb +3 -6
  171. data/app/views/headmin/filters/_date.html.erb +9 -13
  172. data/app/views/headmin/filters/_flatpickr.html.erb +57 -0
  173. data/app/views/headmin/filters/_search.html.erb +5 -5
  174. data/app/views/headmin/filters/_select.html.erb +6 -6
  175. data/app/views/headmin/filters/filter/_button.html.erb +7 -7
  176. data/app/views/headmin/filters/filter/_template.html.erb +1 -1
  177. data/app/views/headmin/forms/_actions.html.erb +1 -1
  178. data/app/views/headmin/forms/_base.html.erb +100 -46
  179. data/app/views/headmin/forms/_blocks.html.erb +4 -4
  180. data/app/views/headmin/forms/_checkbox.html.erb +4 -4
  181. data/app/views/headmin/forms/_date.html.erb +30 -34
  182. data/app/views/headmin/forms/_date_range.html.erb +42 -0
  183. data/app/views/headmin/forms/_email.html.erb +35 -26
  184. data/app/views/headmin/forms/_file.html.erb +186 -34
  185. data/app/views/headmin/forms/_flatpickr.html.erb +34 -0
  186. data/app/views/headmin/forms/_flatpickr_range.html.erb +50 -0
  187. data/app/views/headmin/forms/_hidden.html.erb +24 -0
  188. data/app/views/headmin/forms/_image.html.erb +11 -45
  189. data/app/views/headmin/forms/_label.html.erb +18 -12
  190. data/app/views/headmin/forms/_number.html.erb +40 -37
  191. data/app/views/headmin/forms/_password.html.erb +37 -58
  192. data/app/views/headmin/forms/_redactorx.html.erb +8 -7
  193. data/app/views/headmin/forms/_repeater.html.erb +7 -7
  194. data/app/views/headmin/forms/_select.html.erb +43 -45
  195. data/app/views/headmin/forms/_text.html.erb +94 -59
  196. data/app/views/headmin/forms/_textarea.html.erb +37 -26
  197. data/app/views/headmin/forms/_url.html.erb +35 -26
  198. data/app/views/headmin/forms/_validation.html.erb +3 -3
  199. data/app/views/headmin/forms/_video.html.erb +21 -0
  200. data/app/views/headmin/forms/actions/_destroy.html.erb +3 -3
  201. data/app/views/headmin/forms/fields/_base.html.erb +3 -3
  202. data/app/views/headmin/forms/fields/_file.html.erb +2 -2
  203. data/app/views/headmin/forms/fields/_group.html.erb +4 -4
  204. data/app/views/headmin/forms/fields/_image.html.erb +2 -2
  205. data/app/views/headmin/forms/fields/_list.html.erb +3 -3
  206. data/app/views/headmin/forms/fields/_text.html.erb +2 -2
  207. data/app/views/headmin/forms/repeater/_row.html.erb +1 -1
  208. data/app/views/headmin/heading/_title.html.erb +1 -1
  209. data/app/views/headmin/layout/_body.html.erb +1 -1
  210. data/app/views/headmin/layout/_content.html.erb +1 -1
  211. data/app/views/headmin/layout/_footer.html.erb +1 -1
  212. data/app/views/headmin/layout/_header.html.erb +1 -1
  213. data/app/views/headmin/layout/_main.html.erb +12 -6
  214. data/app/views/headmin/layout/_sidebar.html.erb +3 -3
  215. data/app/views/headmin/layout/sidebar/_bottom.html.erb +1 -1
  216. data/app/views/headmin/layout/sidebar/_nav.html.erb +1 -1
  217. data/app/views/headmin/nav/_dropdown.html.erb +34 -0
  218. data/app/views/headmin/nav/_item.html.erb +22 -13
  219. data/app/views/headmin/nav/item/_devise.html.erb +1 -1
  220. data/app/views/headmin/table/_actions.html.erb +3 -5
  221. data/app/views/headmin/table/_body.html.erb +1 -1
  222. data/app/views/headmin/table/_foot.html.erb +1 -1
  223. data/app/views/headmin/table/_footer.html.erb +1 -1
  224. data/app/views/headmin/table/_head.html.erb +1 -1
  225. data/app/views/headmin/table/_header.html.erb +1 -1
  226. data/app/views/headmin/table/actions/_action.html.erb +4 -4
  227. data/app/views/headmin/table/actions/_delete.html.erb +1 -1
  228. data/app/views/headmin/table/actions/_export.html.erb +1 -1
  229. data/app/views/headmin/table/body/_row.html.erb +15 -7
  230. data/app/views/headmin/views/devise/registrations/_edit.html.erb +2 -2
  231. data/config/importmap.rb +2 -0
  232. data/config/initializers/customize_input_error.rb +4 -4
  233. data/config/locales/en.yml +0 -3
  234. data/config/locales/headmin/forms/en.yml +5 -0
  235. data/config/locales/headmin/forms/nl.yml +5 -0
  236. data/config/locales/headmin/popup/en.yml +4 -0
  237. data/config/locales/headmin/popup/nl.yml +4 -0
  238. data/config/locales/headmin/thumbnail/en.yml +4 -0
  239. data/config/locales/headmin/thumbnail/nl.yml +4 -0
  240. data/config/locales/nl.yml +0 -3
  241. data/esbuild-css.js +25 -0
  242. data/esbuild-js.js +11 -0
  243. data/headmin.gemspec +4 -1
  244. data/lib/generators/headmin/blocks_generator.rb +8 -8
  245. data/lib/generators/headmin/devise_generator.rb +4 -4
  246. data/lib/generators/headmin/fields_generator.rb +9 -9
  247. data/lib/generators/templates/controllers/auth/confirmations_controller.rb +1 -1
  248. data/lib/generators/templates/controllers/auth/omniauth_callbacks_controller.rb +1 -1
  249. data/lib/generators/templates/controllers/auth/passwords_controller.rb +1 -1
  250. data/lib/generators/templates/controllers/auth/registrations_controller.rb +1 -1
  251. data/lib/generators/templates/controllers/auth/sessions_controller.rb +1 -1
  252. data/lib/generators/templates/controllers/auth/unlocks_controller.rb +1 -1
  253. data/lib/generators/templates/models/block.rb +1 -1
  254. data/lib/headmin/engine.rb +18 -3
  255. data/lib/headmin/version.rb +1 -1
  256. data/lib/headmin.rb +1 -1
  257. data/package.json +17 -34
  258. data/src/js/headmin.js +1 -1
  259. data/src/scss/headmin.scss +1 -61
  260. data/yarn.lock +234 -5275
  261. metadata +198 -36
  262. data/.nvmrc +0 -1
  263. data/.rubocop.yml +0 -13
  264. data/app/controllers/concerns/headmin/ckeditor.rb +0 -27
  265. data/app/views/headmin/forms/_ckeditor.html.erb +0 -42
  266. data/dist/css/headmin.css +0 -12354
  267. data/dist/js/headmin.js +0 -1080
  268. data/src/js/headmin/headmin.js +0 -141
  269. data/src/scss/headmin/utilities.scss +0 -19
  270. data/webpack.config.js +0 -30
@@ -0,0 +1,244 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+
3
+ // References:
4
+ // https://developer.mozilla.org/en-US/docs/Web/API/FileReader
5
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
6
+
7
+ export default class extends Controller {
8
+ static get targets() {
9
+ return ["thumbnails", "template", "input", "placeholder", "thumbnail", "thumbnailDestroy"]
10
+ }
11
+
12
+ connect() {
13
+ this.thumbnailWidth = this.firstThumbnailWidth()
14
+ this.thumbnailHeight = this.firstThumbnailHeight()
15
+ }
16
+
17
+ preview() {
18
+ this.removeThumbnails()
19
+ this.addThumbnails()
20
+ this.togglePlaceholder()
21
+ }
22
+
23
+ togglePlaceholder() {
24
+ if (this.hasFilesSelected()) {
25
+ this.hidePlaceholder()
26
+ } else {
27
+ this.showPlaceholder()
28
+ }
29
+ }
30
+
31
+ hasFilesSelected() {
32
+ return this.hasInputFiles() || this.hasVisibleAttachments()
33
+ }
34
+
35
+ hasVisibleAttachments() {
36
+ return this.visibleAttachments().length > 0
37
+ }
38
+
39
+ hasAttachments() {
40
+ return this.attachments().length > 0
41
+ }
42
+
43
+ hasInputFiles() {
44
+ return this.inputFiles().length > 0
45
+ }
46
+
47
+ attachments() {
48
+ return this.thumbnailTargets.filter((thumbnail) => {
49
+ return this.isAttachment(thumbnail)
50
+ })
51
+ }
52
+
53
+ visibleAttachments() {
54
+ return this.attachments().filter((thumbnail) => {
55
+ return !thumbnail.classList.contains('d-none')
56
+ })
57
+ }
58
+
59
+ inputFiles() {
60
+ return Array.from(this.inputTarget.files)
61
+ }
62
+
63
+ writeInputFiles(files) {
64
+ const dataTransfer = new DataTransfer();
65
+ files.forEach((file) => {
66
+ dataTransfer.items.add(file);
67
+ })
68
+ this.inputTarget.files = dataTransfer.files;
69
+ }
70
+
71
+ remove(event) {
72
+ const thumbnail = event.target.closest('[data-file-preview-target="thumbnail"]')
73
+ this.removeThumbnail(thumbnail, event.params.name)
74
+ this.togglePlaceholder()
75
+ }
76
+
77
+ isAttachment(thumbnail) {
78
+ return thumbnail.querySelector('[data-file-preview-target="thumbnailDestroy"]')
79
+ }
80
+
81
+ removeInputFile(thumbnail, fileName) {
82
+ let files = this.inputFiles()
83
+ files = files.filter((file) => {
84
+ return file.name !== fileName
85
+ })
86
+ thumbnail.remove()
87
+ this.writeInputFiles(files)
88
+ }
89
+
90
+ removeAttachment(thumbnail) {
91
+ const destroyInput = thumbnail.querySelector('[data-file-preview-target="thumbnailDestroy"]')
92
+ destroyInput.value = '1';
93
+ thumbnail.classList.add('d-none')
94
+ }
95
+
96
+ showPlaceholder() {
97
+ this.placeholderTarget.classList.remove('d-none')
98
+ }
99
+
100
+ hidePlaceholder() {
101
+ this.placeholderTarget.classList.add('d-none')
102
+ }
103
+
104
+ removeThumbnails() {
105
+ this.thumbnailTargets.forEach((thumbnail) => {
106
+ this.removeThumbnail(thumbnail)
107
+ })
108
+ }
109
+
110
+ removeThumbnail(thumbnail, filename) {
111
+ if (this.isAttachment(thumbnail)) {
112
+ this.removeAttachment(thumbnail)
113
+ } else {
114
+ this.removeInputFile(thumbnail, filename)
115
+ }
116
+ }
117
+
118
+ addThumbnails() {
119
+ const files = this.inputFiles()
120
+ files.forEach((file) => {
121
+ let thumbnail = this.generateDummyThumbnail()
122
+ this.appendThumbnail(thumbnail)
123
+ this.updateThumbnail(this.lastThumbnail(), file)
124
+ })
125
+ }
126
+
127
+ generateDummyThumbnail() {
128
+ return this.templateTarget.content.cloneNode(true)
129
+ }
130
+
131
+ appendThumbnail(thumbnail) {
132
+ return this.thumbnailsTarget.appendChild(thumbnail)
133
+ }
134
+
135
+ updateThumbnail(thumbnail, file) {
136
+ this.updateThumbnailRemoveButton(thumbnail, file.name)
137
+
138
+ const title = this.titleForFile(file)
139
+ this.updateThumbnailTitle(thumbnail, title)
140
+
141
+ const icon = this.iconForMimeType(file.type)
142
+ this.updateThumbnailIcon(thumbnail, icon)
143
+
144
+ if (this.isImage(file)) {
145
+ this.updateThumbnailImage(thumbnail, file)
146
+ }
147
+
148
+ }
149
+
150
+ updateThumbnailImage(thumbnail, file) {
151
+ this.fileToBase64(file).then(base64 => {
152
+ this.updateThumbnailBackground(thumbnail, base64)
153
+ this.removeThumbnailIcon(thumbnail)
154
+ })
155
+ }
156
+
157
+ titleForFile(file) {
158
+ const byteSizeString = this.bytesToString(file.size)
159
+ return `${file.name} (${byteSizeString})`
160
+ }
161
+
162
+ bytesToString(bytes) {
163
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
164
+ return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
165
+ }
166
+
167
+ updateThumbnailRemoveButton(thumbnail, fileName) {
168
+ const removeButton = thumbnail.querySelector('.h-form-file-thumbnail-remove')
169
+ if(removeButton) {
170
+ removeButton.dataset.filePreviewNameParam = fileName
171
+ }
172
+ }
173
+
174
+ updateThumbnailTitle(thumbnail, title) {
175
+ thumbnail.title = title
176
+ }
177
+
178
+ updateThumbnailBackground(thumbnail, url) {
179
+ let thumbnailBackground = thumbnail.querySelector('.h-thumbnail-bg')
180
+ thumbnailBackground.style.backgroundImage = `url('${url}')`
181
+ }
182
+
183
+ removeThumbnailIcon(thumbnail) {
184
+ thumbnail.querySelector('.h-thumbnail-bg').innerHTML = ''
185
+ }
186
+
187
+ updateThumbnailIcon(thumbnail, icon) {
188
+ thumbnail.querySelector('.h-thumbnail-bg').innerHTML = icon
189
+ }
190
+
191
+ iconForMimeType(mimeType) {
192
+ const typeMap = {
193
+ image: ['image/bmp', 'image/gif', 'image/vnd.microsoft.icon', 'image/jpeg', 'image/png', 'image/svg+xml', 'image/tiff', 'image/webp'],
194
+ play: ['video/mp4', 'video/mpeg', 'video/ogg', 'video/mp2t', 'video/webm', 'video/3gpp', 'video/3gpp2'],
195
+ music: ['audio/aac', 'audio/midi', 'audio/x-midi', 'audio/mpeg', 'audio/ogg', 'audio/opus', 'audio/wav', 'audio/webm', 'audio/3gpp', 'audio/3gpp2'],
196
+ word: ['application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
197
+ ppt: ['application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'],
198
+ excel: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
199
+ slides: ['application/vnd.oasis.opendocument.presentation'],
200
+ spreadsheet: ['application/vnd.oasis.opendocument.spreadsheet'],
201
+ richtext: ['application/vnd.oasis.opendocument.text'],
202
+ zip: ['application/zip application/x-7z-compressed', 'application/x-bzip application/x-bzip2 application/gzip application/vnd.rar'],
203
+ pdf: ['application/pdf']
204
+ }
205
+
206
+ const icon_name = Object.keys(typeMap).find(key => typeMap[key].includes(mimeType));
207
+ const full_icon_name = ['bi', 'file', 'earmark', icon_name].filter(e => typeof e === 'string' && e !== '').join('-')
208
+
209
+ return `<i class="bi ${full_icon_name} h-thumbnail-icon"></i>`
210
+ }
211
+
212
+ isImage(file) {
213
+ return file.type.match(/^image/) !== null;
214
+ }
215
+
216
+ fileToBase64(file) {
217
+ return new Promise((resolve, reject) => {
218
+ const reader = new FileReader();
219
+ reader.readAsDataURL(file);
220
+ reader.onload = () => resolve(reader.result);
221
+ reader.onerror = error => reject(error);
222
+ });
223
+ }
224
+
225
+ thumbnails() {
226
+ return this.thumbnailsTarget.querySelectorAll('.img-thumbnail')
227
+ }
228
+
229
+ firstThumbnail() {
230
+ return this.thumbnailsTarget.firstElementChild
231
+ }
232
+
233
+ lastThumbnail() {
234
+ return this.thumbnailsTarget.lastElementChild
235
+ }
236
+
237
+ firstThumbnailWidth() {
238
+ return this.firstThumbnail().style.width;
239
+ }
240
+
241
+ firstThumbnailHeight() {
242
+ return this.firstThumbnail().style.height;
243
+ }
244
+ }
@@ -7,12 +7,22 @@ export default class extends Controller {
7
7
 
8
8
  // Attaches controller logic to the element itself
9
9
  // This allows calling controller methods from the element in other controllers
10
- connect () {
10
+ connect() {
11
11
  this.element['controller'] = this
12
+
13
+ // Clicked outside popup
14
+ document.addEventListener('click', (event) => {
15
+ this.handleOutsideClick(event)
16
+ })
17
+ }
18
+
19
+ handleOutsideClick(event) {
20
+ if (!this.isClickedInside(event)) {
21
+ this.close()
22
+ }
12
23
  }
13
24
 
14
25
  toggle(event) {
15
- event.preventDefault()
16
26
  const expanded = this.buttonTarget.getAttribute('aria-expanded') === 'true'
17
27
  if (expanded) {
18
28
  this.close(null)
@@ -27,10 +37,6 @@ export default class extends Controller {
27
37
  }
28
38
 
29
39
  close(event) {
30
- if (this.isClickedInside(event)) {
31
- event.preventDefault()
32
- return
33
- }
34
40
  this.buttonTarget.setAttribute('aria-expanded', 'false')
35
41
  this.popupTarget.classList.add('closed')
36
42
  }
@@ -0,0 +1,39 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+ import flatpickr from "flatpickr";
3
+ import {Dutch} from "flatpickr/dist/esm/l10n/nl.js"
4
+ import I18n from "../config/i18n";
5
+
6
+ export default class extends Controller {
7
+ static get targets() {
8
+ return ["input"]
9
+ }
10
+
11
+ connect() {
12
+ const options = {...this.defaultOptions(), ...this.options()}
13
+ flatpickr(this.inputTarget, options);
14
+ }
15
+
16
+ options() {
17
+ return JSON.parse(this.inputTarget.getAttribute('data-flatpickr-options'))
18
+ }
19
+
20
+ defaultOptions() {
21
+ return {
22
+ allowInput: true,
23
+ dateFormat: 'd/m/Y',
24
+ locale: this.getLocale(I18n.locale)
25
+ }
26
+ }
27
+
28
+ getLocale(locale) {
29
+ const locales = this.locales()
30
+ return locales[locale]
31
+ }
32
+
33
+ locales() {
34
+ return {
35
+ en: null,
36
+ nl: Dutch
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,7 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ connect() {
5
+ this.element.textContent = "Hello world"
6
+ }
7
+ }
@@ -0,0 +1,8 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+ import {Toast} from "bootstrap";
3
+
4
+ export default class extends Controller {
5
+ connect() {
6
+ new Toast(this.element, {})
7
+ }
8
+ }
@@ -1,5 +1,4 @@
1
1
  import {Controller} from "@hotwired/stimulus"
2
- import Sortable from "sortablejs";
3
2
  import {createPopper} from '@popperjs/core';
4
3
 
5
4
  export default class extends Controller {
@@ -0,0 +1,13 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ connect() {
5
+ if (typeof RedactorX == 'undefined') {
6
+ console.error("RedactorX is a paid module and is not included in Headmin. Please purchase it and import it as a JS module")
7
+ return false;
8
+ }
9
+
10
+ const options = JSON.parse(this.element.getAttribute('data-redactor-options'))
11
+ RedactorX(this.element, options);
12
+ }
13
+ }
@@ -99,7 +99,6 @@ export default class extends Controller {
99
99
  }
100
100
 
101
101
  replaceIdsWithTimestamps(template) {
102
- console.log(template)
103
102
  const regex = new RegExp(template.dataset.templateIdRegex, "g")
104
103
  return template.innerHTML.replace(regex, new Date().getTime())
105
104
  }
@@ -0,0 +1,48 @@
1
+ import {Controller} from "@hotwired/stimulus"
2
+ import TomSelect from "tom-select";
3
+ import I18n from "../config/i18n";
4
+
5
+ export default class extends Controller {
6
+ connect() {
7
+ if (this.element.hasAttribute('multiple')) {
8
+ this.initTomSelect()
9
+ }
10
+ }
11
+
12
+ defaultOptions(locale) {
13
+ const defaultOptions = {
14
+ en: {
15
+ render: {
16
+ option_create: function (data, escape) {
17
+ return '<div class="create">Add <strong>' + escape(data.input) + '</strong>&hellip;</div>'
18
+ },
19
+ no_results: function (data, escape) {
20
+ return '<div class="no-results">No results found</div>'
21
+ }
22
+ }
23
+ },
24
+ nl: {
25
+ render: {
26
+ option_create: function (data, escape) {
27
+ return '<div class="create">Voeg <strong>' + escape(data.input) + '</strong> toe &hellip;</div>'
28
+ },
29
+ no_results: function (data, escape) {
30
+ return '<div class="no-results">Geen resultaten gevonden</div>'
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return defaultOptions[locale]
36
+ }
37
+
38
+ hasTags() {
39
+ return this.element.dataset['tags'] === "true"
40
+ }
41
+
42
+ initTomSelect() {
43
+ const defaultOptions = this.defaultOptions(I18n.locale)
44
+ const options = {create: this.hasTags()}
45
+
46
+ new TomSelect(this.element, {...defaultOptions, ...options})
47
+ }
48
+ }
@@ -1,21 +1,15 @@
1
1
  import {Controller} from "@hotwired/stimulus"
2
2
 
3
3
  export default class extends Controller {
4
- static get values() {
5
- return {
6
- count: {type: Number, default: 0}
7
- }
8
- }
9
-
10
4
  static get targets() {
11
5
  return ["wrapper", "form", "select", "method", "button", "idInputTemplate", "id", "counter"]
12
6
  }
13
7
 
14
8
  connect() {
15
9
  this.wrapperTarget.addEventListener('idSelectionChanged', (event) => {
16
- const ids = event.detail.ids
17
- this.updateIdFields(ids)
18
- this.updateCountValueWithIds(ids)
10
+ this.updateIdFields(event.detail.ids)
11
+ this.updateCounter(event.detail.count)
12
+ this.toggleCounter(event.detail.count)
19
13
  })
20
14
 
21
15
  }
@@ -30,56 +24,32 @@ export default class extends Controller {
30
24
  updateIdFields(ids) {
31
25
  this.removeIds()
32
26
  if (ids instanceof Array) {
33
- this.countValue = ids.length
34
27
  ids.forEach((id) => {
35
28
  this.addId(id)
36
29
  })
37
30
  } else {
38
- this.countValue = Infinity
39
31
  this.addId('')
40
32
  }
41
33
  }
42
34
 
43
- updateCounter() {
35
+ updateCounter(count) {
44
36
  let htmlString = ''
45
- switch (this.countValue) {
37
+ switch (count) {
46
38
  case 0:
47
39
  htmlString = this.counterTarget.getAttribute('data-items-zero')
48
40
  break;
49
41
  case 1:
50
42
  htmlString = this.counterTarget.getAttribute('data-items-one')
51
43
  break;
52
- case Infinity:
53
- htmlString = this.counterTarget.getAttribute('data-items-other')
54
- htmlString = htmlString.replace(/<b>[\s\S]*?<\/b>/, '<b>' + this.totalCount() + '<\/b>');
55
- break;
56
44
  default:
57
45
  htmlString = this.counterTarget.getAttribute('data-items-other')
58
- let count = this.countValue === Infinity ? this.totalCount() : this.countValue
59
46
  htmlString = htmlString.replace(/<b>[\s\S]*?<\/b>/, `<b>${count}<\/b>`);
60
47
  }
61
48
  this.counterTarget.innerHTML = htmlString;
62
49
  }
63
50
 
64
- totalCount() {
65
- return this.counterTarget.getAttribute('data-total-count')
66
- }
67
-
68
- updateCountValueWithIds(ids) {
69
- if (ids instanceof Array) {
70
- this.countValue = ids.length
71
- } else {
72
- this.countValue = Infinity
73
- }
74
- }
75
-
76
- countValueChanged() {
77
- this.updateCounter()
78
- this.toggle()
79
- }
80
-
81
- toggle() {
82
- if (this.countValue > 0) {
51
+ toggleCounter(count) {
52
+ if (count > 0) {
83
53
  this.wrapperTarget.classList.remove('d-none')
84
54
  } else {
85
55
  this.wrapperTarget.classList.add('d-none')
@@ -99,9 +69,9 @@ export default class extends Controller {
99
69
  const option = this.selectedOption()
100
70
  const confirm = option.dataset.confirm
101
71
  if (confirm) {
102
- this.buttonTarget.dataset.confirm = confirm
72
+ this.formTarget.dataset.turboConfirm = confirm
103
73
  } else {
104
- this.buttonTarget.removeAttribute('data-confirm')
74
+ this.formTarget.removeAttribute('data-turbo-confirm')
105
75
  }
106
76
  this.enableButton()
107
77
  }
@@ -1,37 +1,55 @@
1
1
  import {Controller} from "@hotwired/stimulus"
2
2
  import Sortable from "sortablejs";
3
- import Rails from "@rails/ujs";
4
3
 
5
4
  export default class extends Controller {
6
5
  static get values() {
7
- return {url: String}
6
+ return {
7
+ url: String,
8
+ count: Number
9
+ }
8
10
  }
9
11
 
10
12
  static get targets() {
11
- return ["table", "body", "actions", "idCheckbox", "idsCheckbox"]
13
+ return ["table", "body", "actions", "idCheckbox", "idsCheckbox", "row"]
12
14
  }
13
15
 
14
16
  connect() {
15
17
  new Sortable(this.bodyTarget, {
16
18
  handle: '.table-drag-sort-handle',
17
19
  onEnd: (event) => {
18
- Rails.ajax({
19
- url: this.urlValue,
20
- type: "PATCH",
21
- data: this.getIdsDataString(),
22
- });
20
+ this.submitPositions()
23
21
  }
24
22
  })
25
23
  }
26
24
 
27
- getIdsDataString() {
28
- const table = this.tableTarget
29
- let data = ""
30
- const handles = [...table.querySelectorAll(".table-drag-sort-handle")]
31
- handles.map(handle => {
32
- data += `ids[]=${handle.dataset.id}&`
25
+ submitPositions() {
26
+ fetch(this.urlValue, {
27
+ method: 'PATCH',
28
+ body: this.idsFormData(),
29
+ headers: {
30
+ 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
31
+ },
32
+ }).then((response) => {
33
+ return response.text()
34
+ }).catch((err) => {
35
+ console.warn('Fetch went wrong', err)
36
+ })
37
+ }
38
+
39
+ positions() {
40
+ const handles = [...this.tableTarget.querySelectorAll(".table-drag-sort-handle")]
41
+ return handles.map((handle) => {
42
+ return handle.dataset.id
33
43
  })
34
- return data
44
+ }
45
+
46
+ idsFormData() {
47
+ let formData = new FormData()
48
+ console.log(this.positions())
49
+ this.positions().forEach(id => {
50
+ formData.append('ids[]', id)
51
+ })
52
+ return formData
35
53
  }
36
54
 
37
55
  toggleIds(event) {
@@ -52,13 +70,30 @@ export default class extends Controller {
52
70
  'idSelectionChanged',
53
71
  {
54
72
  detail: {
55
- ids: this.ids()
73
+ ids: this.ids(),
74
+ count: this.selectedIdsCount()
56
75
  }
57
76
  }
58
77
  )
59
78
  )
60
79
  }
61
80
 
81
+ selectedIdsCount() {
82
+ if (this.ids() instanceof Array) {
83
+ return this.ids().length
84
+ } else {
85
+ return this.totalCount()
86
+ }
87
+ }
88
+
89
+ totalCount() {
90
+ if (this.countValue === 0) {
91
+ return this.rowTargets.length
92
+ } else {
93
+ return this.countValue
94
+ }
95
+ }
96
+
62
97
  ids() {
63
98
  if (this.idsCheckboxTarget.checked) {
64
99
  return null
@@ -0,0 +1,37 @@
1
+ import {Application} from "@hotwired/stimulus"
2
+ import BlocksController from "./controllers/blocks_controller"
3
+ import DateRangeController from "./controllers/date_range_controller"
4
+ import DropzoneController from "./controllers/dropzone_controller"
5
+ import FilePreviewController from "./controllers/file_preview_controller"
6
+ import FilterController from "./controllers/filter_controller"
7
+ import FiltersController from "./controllers/filters_controller"
8
+ import FlatpickrController from "./controllers/flatpickr_controller"
9
+ import HelloController from "./controllers/hello_controller"
10
+ import NotificationController from "./controllers/notification_controller"
11
+ import PopupController from "./controllers/popup_controller"
12
+ import RedactorxController from "./controllers/redactorx_controller"
13
+ import RepeaterController from "./controllers/repeater_controller"
14
+ import SelectController from "./controllers/select_controller"
15
+ import TableActionsController from "./controllers/table_actions_controller"
16
+ import TableController from "./controllers/table_controller"
17
+
18
+ export class Headmin {
19
+ static start() {
20
+ window.Stimulus = window.Stimulus || Application.start()
21
+ Stimulus.register("blocks", BlocksController)
22
+ Stimulus.register("date_range", DateRangeController)
23
+ Stimulus.register("dropzone", DropzoneController)
24
+ Stimulus.register("file-preview", FilePreviewController)
25
+ Stimulus.register("filter", FilterController)
26
+ Stimulus.register("filters", FiltersController)
27
+ Stimulus.register("flatpickr", FlatpickrController)
28
+ Stimulus.register("hello", HelloController)
29
+ Stimulus.register("notification", NotificationController)
30
+ Stimulus.register("popup", PopupController)
31
+ Stimulus.register("redactorx", RedactorxController)
32
+ Stimulus.register("repeater", RepeaterController)
33
+ Stimulus.register("select", SelectController)
34
+ Stimulus.register("table", TableController)
35
+ Stimulus.register("table-actions", TableActionsController)
36
+ }
37
+ }