headmin 0.3.4 → 0.4.0

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 (157) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +27 -0
  3. data/.gitignore +14 -0
  4. data/.nvmrc +1 -0
  5. data/CHANGELOG.md +24 -0
  6. data/Gemfile +7 -4
  7. data/Gemfile.lock +171 -3
  8. data/README.md +7 -0
  9. data/Rakefile +1 -7
  10. data/app/assets/javascripts/headmin/config/i18n.js +9 -9
  11. data/app/assets/javascripts/headmin/controllers/autocomplete_controller.js +255 -0
  12. data/app/assets/javascripts/headmin/controllers/blocks_controller.js +74 -79
  13. data/app/assets/javascripts/headmin/controllers/date_range_controller.js +24 -24
  14. data/app/assets/javascripts/headmin/controllers/dropzone_controller.js +23 -25
  15. data/app/assets/javascripts/headmin/controllers/file_preview_controller.js +237 -237
  16. data/app/assets/javascripts/headmin/controllers/filter_controller.js +44 -44
  17. data/app/assets/javascripts/headmin/controllers/filters_controller.js +57 -61
  18. data/app/assets/javascripts/headmin/controllers/flatpickr_controller.js +29 -29
  19. data/app/assets/javascripts/headmin/controllers/hello_controller.js +3 -3
  20. data/app/assets/javascripts/headmin/controllers/notification_controller.js +7 -6
  21. data/app/assets/javascripts/headmin/controllers/popup_controller.js +51 -52
  22. data/app/assets/javascripts/headmin/controllers/redactorx_controller.js +36 -9
  23. data/app/assets/javascripts/headmin/controllers/repeater_controller.js +122 -125
  24. data/app/assets/javascripts/headmin/controllers/select_controller.js +40 -39
  25. data/app/assets/javascripts/headmin/controllers/table_actions_controller.js +100 -101
  26. data/app/assets/javascripts/headmin/controllers/table_controller.js +115 -115
  27. data/app/assets/javascripts/headmin/index.js +38 -35
  28. data/app/assets/javascripts/headmin.js +243 -34
  29. data/app/assets/stylesheets/headmin/forms/autocomplete.scss +21 -0
  30. data/app/assets/stylesheets/headmin/forms/file.scss +46 -0
  31. data/app/assets/stylesheets/headmin/forms/repeater.scss +62 -0
  32. data/app/assets/stylesheets/headmin/forms/search.scss +12 -0
  33. data/app/assets/stylesheets/headmin/forms.scss +11 -0
  34. data/app/assets/stylesheets/headmin/general.scss +5 -0
  35. data/app/assets/stylesheets/headmin/overrides/bootstrap.scss +5 -3
  36. data/app/assets/stylesheets/headmin/overrides/redactorx.scss +74 -0
  37. data/app/assets/stylesheets/headmin/popup.scss +1 -0
  38. data/app/assets/stylesheets/headmin/syntax.scss +36 -349
  39. data/app/assets/stylesheets/headmin/table.scss +1 -1
  40. data/app/assets/stylesheets/headmin/utilities/buttons.scss +19 -0
  41. data/app/assets/stylesheets/headmin/utilities/dropzone.scss +72 -0
  42. data/app/assets/stylesheets/headmin/utilities.scss +2 -68
  43. data/app/assets/stylesheets/headmin.css +205 -205
  44. data/app/assets/stylesheets/headmin.scss +1 -1
  45. data/app/helpers/headmin/admin_helper.rb +0 -1
  46. data/app/helpers/headmin/form_helper.rb +2 -8
  47. data/app/models/concerns/headmin/blockable.rb +1 -1
  48. data/app/models/concerns/headmin/field.rb +1 -0
  49. data/app/models/concerns/headmin/form/autocompletable.rb +38 -0
  50. data/app/models/concerns/headmin/form/hintable.rb +19 -0
  51. data/app/models/concerns/headmin/form/input_groupable.rb +23 -0
  52. data/app/models/concerns/headmin/form/labelable.rb +33 -0
  53. data/app/models/concerns/headmin/form/listable.rb +28 -0
  54. data/app/models/concerns/headmin/form/placeholderable.rb +13 -0
  55. data/app/models/concerns/headmin/form/validatable.rb +40 -0
  56. data/app/models/concerns/headmin/form/wrappable.rb +21 -0
  57. data/app/models/headmin/.DS_Store +0 -0
  58. data/app/models/headmin/blocks_view.rb +15 -0
  59. data/app/models/headmin/form/blocks_view.rb +29 -0
  60. data/app/models/headmin/form/checkbox_view.rb +52 -0
  61. data/app/models/headmin/form/date_range_view.rb +25 -0
  62. data/app/models/headmin/form/date_view.rb +45 -0
  63. data/app/models/headmin/form/email_view.rb +48 -0
  64. data/app/models/headmin/form/file_view.rb +116 -0
  65. data/app/models/headmin/form/flatpickr_range_view.rb +102 -0
  66. data/app/models/headmin/form/flatpickr_view.rb +37 -0
  67. data/app/models/headmin/form/hidden_view.rb +10 -0
  68. data/app/models/headmin/form/hint_view.rb +6 -0
  69. data/app/models/headmin/form/input_group_view.rb +19 -0
  70. data/app/models/headmin/form/label_view.rb +24 -0
  71. data/app/models/headmin/form/number_view.rb +49 -0
  72. data/app/models/headmin/form/password_view.rb +44 -0
  73. data/app/models/headmin/form/redactorx_view.rb +59 -0
  74. data/app/models/headmin/form/search_view.rb +48 -0
  75. data/app/models/headmin/form/select_view.rb +62 -0
  76. data/app/models/headmin/form/switch_view.rb +23 -0
  77. data/app/models/headmin/form/text_view.rb +48 -0
  78. data/app/models/headmin/form/textarea_view.rb +44 -0
  79. data/app/models/headmin/form/url_view.rb +48 -0
  80. data/app/models/headmin/form/wrapper_view.rb +19 -0
  81. data/app/models/headmin/form/wysiwyg_view.rb +17 -0
  82. data/app/models/headmin/{thumbnail.rb → thumbnail_view.rb} +6 -1
  83. data/app/models/view_model.rb +58 -0
  84. data/app/views/headmin/_blocks.html.erb +13 -9
  85. data/app/views/headmin/_heading.html.erb +7 -1
  86. data/app/views/headmin/_thumbnail.html.erb +1 -37
  87. data/app/views/headmin/forms/_autocomplete.html.erb +11 -0
  88. data/app/views/headmin/forms/_blocks.html.erb +16 -17
  89. data/app/views/headmin/forms/_checkbox.html.erb +24 -29
  90. data/app/views/headmin/forms/_datalist.html.erb +3 -0
  91. data/app/views/headmin/forms/_date.html.erb +24 -24
  92. data/app/views/headmin/forms/_date_range.html.erb +19 -21
  93. data/app/views/headmin/forms/_email.html.erb +27 -32
  94. data/app/views/headmin/forms/_errors.html.erb +2 -3
  95. data/app/views/headmin/forms/_file.html.erb +84 -181
  96. data/app/views/headmin/forms/_flatpickr.html.erb +19 -20
  97. data/app/views/headmin/forms/_flatpickr_range.html.erb +28 -37
  98. data/app/views/headmin/forms/_hidden.html.erb +9 -10
  99. data/app/views/headmin/forms/_hint.html.erb +16 -0
  100. data/app/views/headmin/forms/_input_group.html.erb +21 -0
  101. data/app/views/headmin/forms/_label.html.erb +5 -13
  102. data/app/views/headmin/forms/_number.html.erb +23 -35
  103. data/app/views/headmin/forms/_password.html.erb +21 -30
  104. data/app/views/headmin/forms/_redactorx.html.erb +21 -40
  105. data/app/views/headmin/forms/_repeater.html.erb +55 -60
  106. data/app/views/headmin/forms/_search.html.erb +43 -0
  107. data/app/views/headmin/forms/_select.html.erb +24 -49
  108. data/app/views/headmin/forms/_switch.html.erb +29 -0
  109. data/app/views/headmin/forms/_text.html.erb +42 -96
  110. data/app/views/headmin/forms/_textarea.html.erb +21 -32
  111. data/app/views/headmin/forms/_url.html.erb +26 -31
  112. data/app/views/headmin/forms/_validation.html.erb +10 -13
  113. data/app/views/headmin/forms/_wrapper.html.erb +9 -0
  114. data/app/views/headmin/forms/_wysiwyg.html.erb +28 -0
  115. data/app/views/headmin/forms/autocomplete/_item.html.erb +3 -0
  116. data/app/views/headmin/forms/autocomplete/_list.html.erb +3 -0
  117. data/app/views/headmin/forms/fields/_group.html.erb +5 -3
  118. data/app/views/headmin/forms/repeater/_row.html.erb +4 -4
  119. data/bin/console +0 -1
  120. data/config/locales/headmin/forms/en.yml +0 -11
  121. data/config/locales/headmin/forms/nl.yml +0 -11
  122. data/esbuild-css.js +18 -18
  123. data/esbuild-js.js +8 -8
  124. data/headmin.gemspec +1 -3
  125. data/lib/generators/templates/controllers/auth/confirmations_controller.rb +0 -2
  126. data/lib/generators/templates/controllers/auth/omniauth_callbacks_controller.rb +0 -2
  127. data/lib/generators/templates/controllers/auth/passwords_controller.rb +0 -2
  128. data/lib/generators/templates/controllers/auth/registrations_controller.rb +0 -2
  129. data/lib/generators/templates/controllers/auth/sessions_controller.rb +0 -2
  130. data/lib/generators/templates/controllers/auth/unlocks_controller.rb +0 -2
  131. data/lib/headmin/version.rb +1 -3
  132. data/lib/headmin.rb +0 -2
  133. data/package-lock.json +5359 -0
  134. data/package.json +12 -4
  135. data/view_model_benchmark.rb +74 -0
  136. data/yarn-error.log +17 -12
  137. data/yarn.lock +1575 -31
  138. metadata +62 -24
  139. data/app/assets/stylesheets/headmin/form.scss +0 -132
  140. data/app/assets/stylesheets/headmin/overrides/redactorx.css +0 -3
  141. data/app/helpers/headmin/documentation_helper.rb +0 -35
  142. data/app/models/headmin/documentation_renderer.rb +0 -32
  143. data/app/models/headmin/form/base.rb +0 -78
  144. data/app/models/headmin/form/text.rb +0 -51
  145. data/app/services/block_service.rb +0 -72
  146. data/app/views/headmin/_card.html.erb +0 -52
  147. data/app/views/headmin/forms/_actions.html.erb +0 -28
  148. data/app/views/headmin/forms/_base.html.erb +0 -114
  149. data/app/views/headmin/forms/_image.html.erb +0 -21
  150. data/app/views/headmin/forms/_video.html.erb +0 -21
  151. data/app/views/headmin/forms/actions/_destroy.html.erb +0 -13
  152. data/app/views/headmin/forms/actions/_save.html.erb +0 -12
  153. data/app/views/headmin/forms/actions/_view.html.erb +0 -15
  154. data/docs/blocks-and-fields.md +0 -54
  155. data/docs/blocks.md +0 -48
  156. data/docs/devise.md +0 -41
  157. data/docs/fields.md +0 -79
@@ -4962,6 +4962,202 @@ Controller.blessings = [ClassPropertiesBlessing, TargetPropertiesBlessing, Value
4962
4962
  Controller.targets = [];
4963
4963
  Controller.values = {};
4964
4964
 
4965
+ // app/assets/javascripts/headmin/controllers/autocomplete_controller.js
4966
+ var autocomplete_controller_default = class extends Controller {
4967
+ static get targets() {
4968
+ return ["input", "dropdown", "dropdownItem"];
4969
+ }
4970
+ static get values() {
4971
+ return {
4972
+ url: String
4973
+ };
4974
+ }
4975
+ connect() {
4976
+ this.inputTarget.addEventListener("focus", (event) => {
4977
+ this.show();
4978
+ });
4979
+ this.inputTarget.addEventListener("keydown", (event) => {
4980
+ this.handleKeydown(event);
4981
+ });
4982
+ document.addEventListener("click", (event) => {
4983
+ this.handleOutsideClick(event);
4984
+ });
4985
+ }
4986
+ handleKeydown(event) {
4987
+ this.show();
4988
+ const keyCode = parseInt(event.keyCode, 10);
4989
+ if (this.isArrowKey(keyCode)) {
4990
+ this.handleArrowKey(keyCode);
4991
+ } else if (this.isEnterKey(keyCode)) {
4992
+ this.selectActiveItem();
4993
+ } else {
4994
+ this.handleTextKey();
4995
+ }
4996
+ }
4997
+ selectActiveItem() {
4998
+ this.activeItem().click();
4999
+ }
5000
+ isEnterKey(keyCode) {
5001
+ return keyCode === 13;
5002
+ }
5003
+ handleArrowKey(keyCode) {
5004
+ switch (keyCode) {
5005
+ case 38:
5006
+ this.handleArrowUp();
5007
+ break;
5008
+ case 40:
5009
+ this.handleArrowDown();
5010
+ break;
5011
+ default:
5012
+ }
5013
+ }
5014
+ handleArrowUp() {
5015
+ this.selectPreviousItem();
5016
+ }
5017
+ handleArrowDown() {
5018
+ this.selectNextItem();
5019
+ }
5020
+ selectNextItem() {
5021
+ const next = this.nextItem();
5022
+ this.deselectAll();
5023
+ next.classList.add("active");
5024
+ }
5025
+ nextItem() {
5026
+ const current = this.activeItem();
5027
+ if (!this.hasSelectedItem()) {
5028
+ return this.firstItem();
5029
+ }
5030
+ if (this.isItemLast(current)) {
5031
+ return this.firstItem();
5032
+ } else {
5033
+ const index2 = this.itemIndex(current);
5034
+ return this.itemAtIndex(index2 + 1);
5035
+ }
5036
+ }
5037
+ selectPreviousItem() {
5038
+ const previous = this.previousItem();
5039
+ this.deselectAll();
5040
+ previous.classList.add("active");
5041
+ }
5042
+ previousItem() {
5043
+ const current = this.activeItem();
5044
+ if (!this.hasSelectedItem()) {
5045
+ return this.lastItem();
5046
+ }
5047
+ if (this.isItemFirst(current)) {
5048
+ return this.lastItem();
5049
+ } else {
5050
+ const index2 = this.itemIndex(current);
5051
+ return this.itemAtIndex(index2 - 1);
5052
+ }
5053
+ }
5054
+ deselectAll() {
5055
+ this.dropdownItemTargets.forEach((dropdownItem) => {
5056
+ dropdownItem.classList.remove("active");
5057
+ });
5058
+ }
5059
+ itemAtIndex(index2) {
5060
+ return this.dropdownItemTargets[index2];
5061
+ }
5062
+ firstItem() {
5063
+ return this.itemAtIndex(0);
5064
+ }
5065
+ lastItem() {
5066
+ return this.itemAtIndex(this.dropdownItemTargets.length - 1);
5067
+ }
5068
+ hasSelectedItem() {
5069
+ return this.activeItem() !== void 0;
5070
+ }
5071
+ activeItem() {
5072
+ return this.dropdownItemTargets.find((item) => {
5073
+ return item.classList.contains("active");
5074
+ });
5075
+ }
5076
+ isItemLast(item) {
5077
+ return this.itemIndex(item) === this.dropdownItemTargets.length - 1;
5078
+ }
5079
+ isItemFirst(item) {
5080
+ return this.itemIndex(item) === 0;
5081
+ }
5082
+ itemIndex(item) {
5083
+ return Array.from(this.dropdownItemTargets).indexOf(item);
5084
+ }
5085
+ handleTextKey() {
5086
+ this.fetchCollection().then((html) => {
5087
+ this.renderCollection(html);
5088
+ }).then(() => {
5089
+ this.highlight();
5090
+ });
5091
+ }
5092
+ show() {
5093
+ if (this.isDropdownEmpty()) {
5094
+ this.dropdownTarget.classList.remove("d-none");
5095
+ } else {
5096
+ this.hide();
5097
+ }
5098
+ }
5099
+ hide() {
5100
+ this.dropdownTarget.classList.add("d-none");
5101
+ }
5102
+ isDropdownEmpty() {
5103
+ return this.dropdownTarget.textContent.trim().length > 0;
5104
+ }
5105
+ isArrowKey(keyCode) {
5106
+ const arrowKeyCodes = [37, 38, 39, 40];
5107
+ return arrowKeyCodes.includes(keyCode);
5108
+ }
5109
+ fetchCollection() {
5110
+ if (this.isRemote()) {
5111
+ return fetch(this.urlValue).then((response) => {
5112
+ return response.text();
5113
+ }).catch((error2) => {
5114
+ console.error("The URL you provided for the autocomplete collection didn't return a successful result", error2);
5115
+ });
5116
+ } else {
5117
+ return Promise.resolve(this.dropdownTarget.innerHTML);
5118
+ }
5119
+ }
5120
+ renderCollection(html) {
5121
+ this.dropdownTarget.innerHTML = html;
5122
+ }
5123
+ isRemote() {
5124
+ return this.hasUrlValue;
5125
+ }
5126
+ highlight() {
5127
+ const query = this.value();
5128
+ this.dropdownItemTargets.forEach((dropdownItem) => {
5129
+ let text = dropdownItem.innerHTML;
5130
+ text = text.replace(/<mark.*?>(.*?)<\/mark>/ig, "$1");
5131
+ const regex2 = new RegExp(`(${query})`, "gi");
5132
+ text = text.replace(regex2, "<mark>$1</mark>");
5133
+ dropdownItem.innerHTML = text;
5134
+ });
5135
+ }
5136
+ select(event) {
5137
+ this.inputTarget.value = event.target.getAttribute("value");
5138
+ this.hide();
5139
+ }
5140
+ value() {
5141
+ return this.inputTarget.value;
5142
+ }
5143
+ numberOfCharacters() {
5144
+ return this.value().length;
5145
+ }
5146
+ handleOutsideClick(event) {
5147
+ if (!this.isClickedInside(event)) {
5148
+ this.hide();
5149
+ }
5150
+ }
5151
+ isClickedInside(event) {
5152
+ if (!event) {
5153
+ return false;
5154
+ }
5155
+ const inInput = this.inputTarget.contains(event.target);
5156
+ const inDropdown = this.dropdownTarget.contains(event.target);
5157
+ return inInput || inDropdown;
5158
+ }
5159
+ };
5160
+
4965
5161
  // node_modules/sortablejs/modular/sortable.esm.js
4966
5162
  function ownKeys(object, enumerableOnly) {
4967
5163
  var keys = Object.keys(object);
@@ -7142,7 +7338,7 @@ var blocks_controller_default = class extends Controller {
7142
7338
  return ["templateBlock", "block", "blocks", "templateEmpty", "button", "buttons"];
7143
7339
  }
7144
7340
  connect() {
7145
- new sortable_esm_default(this.blocksTarget, {
7341
+ sortable_esm_default.create(this.blocksTarget, {
7146
7342
  onEnd: () => {
7147
7343
  this.reorderPositions();
7148
7344
  }
@@ -7171,7 +7367,6 @@ var blocks_controller_default = class extends Controller {
7171
7367
  } else {
7172
7368
  this.blocksTarget.insertAdjacentHTML("afterbegin", html);
7173
7369
  }
7174
- this.blocksTarget.dispatchEvent(new CustomEvent("headmin:reinit", { bubbles: true }));
7175
7370
  this.reorderPositions();
7176
7371
  this.toggleEmpty();
7177
7372
  }
@@ -7190,8 +7385,7 @@ var blocks_controller_default = class extends Controller {
7190
7385
  }
7191
7386
  setPosition(html) {
7192
7387
  const position = this.retrieveLastPosition() + 1;
7193
- const regex = new RegExp("99999", "g");
7194
- return html.replace(regex, position);
7388
+ return html.replace(/99999/g, position);
7195
7389
  }
7196
7390
  retrieveLastPosition() {
7197
7391
  const blocks = Array.from(this.blockTargets);
@@ -7202,7 +7396,7 @@ var blocks_controller_default = class extends Controller {
7202
7396
  return parseInt(lastBlock.querySelector("[name*='position']").value);
7203
7397
  }
7204
7398
  reorderPositions() {
7205
- for (let [index2, block] of this.blockTargets.entries()) {
7399
+ for (const [index2, block] of this.blockTargets.entries()) {
7206
7400
  this.changePositionInfo(block, index2);
7207
7401
  }
7208
7402
  }
@@ -7248,21 +7442,20 @@ var dropzone_controller_default = class extends Controller {
7248
7442
  return ["input"];
7249
7443
  }
7250
7444
  connect() {
7251
- this.element.classList.add("h-dropzone");
7252
7445
  this.inputTarget.addEventListener("dragover", (event) => {
7253
- this.element.classList.add("dragover");
7446
+ this.element.classList.add("focus");
7254
7447
  });
7255
7448
  this.inputTarget.addEventListener("dragleave", (event) => {
7256
- this.element.classList.remove("dragover");
7449
+ this.element.classList.remove("focus");
7257
7450
  });
7258
7451
  this.inputTarget.addEventListener("drop", (event) => {
7259
- this.element.classList.remove("dragover");
7452
+ this.element.classList.remove("focus");
7260
7453
  });
7261
7454
  this.inputTarget.addEventListener("focusin", (event) => {
7262
- this.element.classList.add("active");
7455
+ this.element.classList.add("focus");
7263
7456
  });
7264
7457
  this.inputTarget.addEventListener("focusout", (event) => {
7265
- this.element.classList.remove("active");
7458
+ this.element.classList.remove("focus");
7266
7459
  });
7267
7460
  }
7268
7461
  };
@@ -7362,7 +7555,7 @@ var file_preview_controller_default = class extends Controller {
7362
7555
  addThumbnails() {
7363
7556
  const files = this.inputFiles();
7364
7557
  files.forEach((file) => {
7365
- let thumbnail = this.generateDummyThumbnail();
7558
+ const thumbnail = this.generateDummyThumbnail();
7366
7559
  this.appendThumbnail(thumbnail);
7367
7560
  this.updateThumbnail(this.lastThumbnail(), file);
7368
7561
  });
@@ -7407,7 +7600,7 @@ var file_preview_controller_default = class extends Controller {
7407
7600
  thumbnail.title = title;
7408
7601
  }
7409
7602
  updateThumbnailBackground(thumbnail, url) {
7410
- let thumbnailBackground = thumbnail.querySelector(".h-thumbnail-bg");
7603
+ const thumbnailBackground = thumbnail.querySelector(".h-thumbnail-bg");
7411
7604
  thumbnailBackground.style.backgroundImage = `url('${url}')`;
7412
7605
  }
7413
7606
  removeThumbnailIcon(thumbnail) {
@@ -7430,9 +7623,9 @@ var file_preview_controller_default = class extends Controller {
7430
7623
  zip: ["application/zip application/x-7z-compressed", "application/x-bzip application/x-bzip2 application/gzip application/vnd.rar"],
7431
7624
  pdf: ["application/pdf"]
7432
7625
  };
7433
- const icon_name = Object.keys(typeMap).find((key) => typeMap[key].includes(mimeType));
7434
- const full_icon_name = ["bi", "file", "earmark", icon_name].filter((e) => typeof e === "string" && e !== "").join("-");
7435
- return `<i class="bi ${full_icon_name} h-thumbnail-icon"></i>`;
7626
+ const iconName = Object.keys(typeMap).find((key) => typeMap[key].includes(mimeType));
7627
+ const fullIconName = ["bi", "file", "earmark", iconName].filter((e) => typeof e === "string" && e !== "").join("-");
7628
+ return `<i class="bi ${fullIconName} h-thumbnail-icon"></i>`;
7436
7629
  }
7437
7630
  isImage(file) {
7438
7631
  return file.type.match(/^image/) !== null;
@@ -7468,7 +7661,7 @@ var filter_controller_default = class extends Controller {
7468
7661
  return ["button", "popup"];
7469
7662
  }
7470
7663
  connect() {
7471
- this.element["controller"] = this;
7664
+ this.element.controller = this;
7472
7665
  document.addEventListener("click", (event) => {
7473
7666
  this.handleOutsideClick(event);
7474
7667
  });
@@ -7524,7 +7717,6 @@ var filters_controller_default = class extends Controller {
7524
7717
  let html = this.getTemplateHTML(name);
7525
7718
  html = this.replaceIdsWithTimestamps(html);
7526
7719
  this.listTarget.insertAdjacentHTML("beforeend", html);
7527
- this.menuItemTarget.dispatchEvent(new CustomEvent("headmin:reinit", { bubbles: true }));
7528
7720
  }
7529
7721
  remove(event) {
7530
7722
  const filter = event.currentTarget.closest(".h-filter");
@@ -7553,8 +7745,7 @@ var filters_controller_default = class extends Controller {
7553
7745
  return template.innerHTML;
7554
7746
  }
7555
7747
  replaceIdsWithTimestamps(html) {
7556
- const regex = new RegExp("template_id", "g");
7557
- return html.replace(regex, new Date().getTime());
7748
+ return html.replace(/template_id/g, new Date().getTime());
7558
7749
  }
7559
7750
  };
7560
7751
 
@@ -9746,7 +9937,7 @@ var flatpickr_controller_default = class extends Controller {
9746
9937
  esm_default(this.inputTarget, options);
9747
9938
  }
9748
9939
  options() {
9749
- return JSON.parse(this.inputTarget.getAttribute("data-flatpickr-options"));
9940
+ return JSON.parse(this.inputTarget.getAttribute("data-flatpickr"));
9750
9941
  }
9751
9942
  defaultOptions() {
9752
9943
  return {
@@ -14853,8 +15044,8 @@ var popup_controller_default = class extends Controller {
14853
15044
  }
14854
15045
  open(event) {
14855
15046
  const button = event.target.closest('[data-popup-target="button"]');
14856
- const popup = this.popupById(button.dataset["popupId"]);
14857
- const passThru = button.dataset["popupPassThru"];
15047
+ const popup = this.popupById(button.dataset.popupId);
15048
+ const passThru = button.dataset.popupPassThru;
14858
15049
  if (passThru) {
14859
15050
  const passThruElement = popup.querySelector(passThru);
14860
15051
  passThruElement.click();
@@ -14865,12 +15056,12 @@ var popup_controller_default = class extends Controller {
14865
15056
  }
14866
15057
  close(event) {
14867
15058
  const button = event.target.closest('[data-popup-target="button"]');
14868
- const popup = this.popupById(button.dataset["popupId"]);
15059
+ const popup = this.popupById(button.dataset.popupId);
14869
15060
  this.closePopup(popup);
14870
15061
  }
14871
15062
  popupById(id) {
14872
15063
  return this.popupTargets.find((popup) => {
14873
- return popup.dataset["popupId"] === id;
15064
+ return popup.dataset.popupId === id;
14874
15065
  });
14875
15066
  }
14876
15067
  openPopup(popup) {
@@ -14884,12 +15075,30 @@ var popup_controller_default = class extends Controller {
14884
15075
  // app/assets/javascripts/headmin/controllers/redactorx_controller.js
14885
15076
  var redactorx_controller_default = class extends Controller {
14886
15077
  connect() {
14887
- if (typeof RedactorX == "undefined") {
15078
+ this.initRedactor();
15079
+ }
15080
+ initRedactor() {
15081
+ if (typeof RedactorX === "undefined") {
14888
15082
  console.error("RedactorX is a paid module and is not included in Headmin. Please purchase it and import it as a JS module");
14889
15083
  return false;
14890
15084
  }
15085
+ const defaultOptions = {
15086
+ editor: {
15087
+ minHeight: "57px"
15088
+ },
15089
+ subscribe: {
15090
+ "app.start": () => {
15091
+ this.stylize();
15092
+ }
15093
+ }
15094
+ };
14891
15095
  const options = JSON.parse(this.element.getAttribute("data-redactor-options"));
14892
- RedactorX(this.element, options);
15096
+ RedactorX(this.element, { ...defaultOptions, ...options });
15097
+ }
15098
+ stylize() {
15099
+ const container = this.element.nextElementSibling;
15100
+ const inputClasses = this.element.classList;
15101
+ container.classList.add(...inputClasses);
14893
15102
  }
14894
15103
  };
14895
15104
 
@@ -14904,7 +15113,7 @@ var repeater_controller_default = class extends Controller {
14904
15113
  return ["repeater", "footer", "template", "row", "list", "empty", "addButton"];
14905
15114
  }
14906
15115
  connect() {
14907
- new sortable_esm_default(this.listTarget, {
15116
+ sortable_esm_default.create(this.listTarget, {
14908
15117
  animation: 150,
14909
15118
  ghostClass: "list-group-item-dark",
14910
15119
  draggable: ".repeater-row",
@@ -14935,7 +15144,7 @@ var repeater_controller_default = class extends Controller {
14935
15144
  event.preventDefault();
14936
15145
  const button = event.target;
14937
15146
  const templateName = button.dataset.templateName;
14938
- let rowIndex = button.dataset.rowIndex;
15147
+ const rowIndex = button.dataset.rowIndex;
14939
15148
  const template = this.getTemplate(templateName);
14940
15149
  const html = this.replaceIdsWithTimestamps(template);
14941
15150
  if (rowIndex) {
@@ -14944,7 +15153,6 @@ var repeater_controller_default = class extends Controller {
14944
15153
  } else {
14945
15154
  this.footerTarget.insertAdjacentHTML("beforebegin", html);
14946
15155
  }
14947
- document.dispatchEvent(new CustomEvent("headmin:reinit", { bubbles: true }));
14948
15156
  this.resetIndices();
14949
15157
  this.resetPositions();
14950
15158
  this.toggleEmpty();
@@ -15036,7 +15244,7 @@ var select_controller_default = class extends Controller {
15036
15244
  return defaultOptions[locale];
15037
15245
  }
15038
15246
  hasTags() {
15039
- return this.element.dataset["tags"] === "true";
15247
+ return this.element.dataset.tags === "true";
15040
15248
  }
15041
15249
  initTomSelect() {
15042
15250
  const defaultOptions = this.defaultOptions(i18n_default.locale);
@@ -15152,7 +15360,7 @@ var table_controller_default = class extends Controller {
15152
15360
  return ["table", "body", "actions", "idCheckbox", "idsCheckbox", "row"];
15153
15361
  }
15154
15362
  connect() {
15155
- new sortable_esm_default(this.bodyTarget, {
15363
+ sortable_esm_default.create(this.bodyTarget, {
15156
15364
  handle: ".table-drag-sort-handle",
15157
15365
  onEnd: (event) => {
15158
15366
  this.submitPositions();
@@ -15179,7 +15387,7 @@ var table_controller_default = class extends Controller {
15179
15387
  });
15180
15388
  }
15181
15389
  idsFormData() {
15182
- let formData = new FormData();
15390
+ const formData = new FormData();
15183
15391
  this.positions().forEach((id) => {
15184
15392
  formData.append("ids[]", id);
15185
15393
  });
@@ -15247,8 +15455,9 @@ var table_controller_default = class extends Controller {
15247
15455
  var Headmin = class {
15248
15456
  static start() {
15249
15457
  window.Stimulus = window.Stimulus || Application.start();
15458
+ Stimulus.register("autocomplete", autocomplete_controller_default);
15250
15459
  Stimulus.register("blocks", blocks_controller_default);
15251
- Stimulus.register("date_range", date_range_controller_default);
15460
+ Stimulus.register("date-range", date_range_controller_default);
15252
15461
  Stimulus.register("dropzone", dropzone_controller_default);
15253
15462
  Stimulus.register("file-preview", file_preview_controller_default);
15254
15463
  Stimulus.register("filter", filter_controller_default);
@@ -0,0 +1,21 @@
1
+ .h-autocomplete {
2
+ position: absolute;
3
+ top: calc(100% + 2px);
4
+ left: 0;
5
+ z-index: $zindex-dropdown;
6
+ width: 100%;
7
+ @include reset-text();
8
+ @include font-size($dropdown-font-size);
9
+ word-wrap: break-word;
10
+ background-color: $dropdown-bg;
11
+ background-clip: padding-box;
12
+ border-radius: $dropdown-border-radius !important;
13
+ @include box-shadow($dropdown-box-shadow);
14
+ border: $dropdown-border-width solid $dropdown-border-color;
15
+ overflow: hidden;
16
+
17
+ .list-group-flush {
18
+ margin: 0;
19
+ padding: 0;
20
+ }
21
+ }
@@ -0,0 +1,46 @@
1
+ .h-form-file {
2
+ .h-form-file-thumbnails {
3
+ margin-bottom: map-get($spacers, 2);
4
+ }
5
+
6
+ &.h-dropzone {
7
+ padding: map-get($spacers, 2);
8
+
9
+ .h-form-file-thumbnails {
10
+ margin-bottom: 0;
11
+ }
12
+ }
13
+ }
14
+
15
+ .h-form-file-thumbnails {
16
+ position: relative;
17
+ display: flex;
18
+ flex-wrap: wrap;
19
+ gap: map-get($spacers, 2);
20
+ }
21
+
22
+ .h-form-file-thumbnail {
23
+ position: relative;
24
+ pointer-events: initial !important;
25
+ display: flex;
26
+ }
27
+
28
+ .h-form-file-thumbnail-remove {
29
+ position: absolute;
30
+ top: 0;
31
+ right: 0;
32
+ background: $danger;
33
+ width: 20px;
34
+ height: 20px;
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ color: white;
39
+ border-radius: $border-radius;
40
+ z-index: 3;
41
+ cursor: pointer;
42
+
43
+ &:hover {
44
+ background: tint-color($danger, $btn-hover-bg-tint-amount)
45
+ }
46
+ }
@@ -0,0 +1,62 @@
1
+ .repeater {
2
+ position: relative;
3
+ }
4
+
5
+ .repeater-row {
6
+ position: relative;
7
+
8
+ &:hover {
9
+ .repeater-row-remove {
10
+ visibility: visible;
11
+ }
12
+
13
+ .repeater-row-add {
14
+ visibility: visible;
15
+ }
16
+
17
+ .repeater-row-handle {
18
+ visibility: visible;
19
+ }
20
+ }
21
+ }
22
+
23
+ .repeater-row-remove {
24
+ position: absolute;
25
+ top: calc(50% - 17px);
26
+ right: -22px;
27
+ z-index: 2;
28
+ visibility: hidden;
29
+
30
+ i.bi {
31
+ background: white;
32
+ border-radius: 50%;
33
+ }
34
+ }
35
+
36
+ .repeater-row-add {
37
+ position: absolute;
38
+ top: calc(100% - 18px);
39
+ right: calc(50% - 17px);
40
+ z-index: 2;
41
+ visibility: hidden;
42
+
43
+ i.bi {
44
+ background: white;
45
+ border-radius: 50%;
46
+ }
47
+ }
48
+
49
+ .repeater-row-handle {
50
+ position: absolute;
51
+ top: 0;
52
+ left: 0;
53
+ height: 100%;
54
+ width: 18px;
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ z-index: 2;
59
+ visibility: hidden;
60
+ cursor: move;
61
+ }
62
+
@@ -0,0 +1,12 @@
1
+ input[type="search"] {
2
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='%236c757d' viewBox='0 0 16 16'%3E%3Cpath d='M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z'/%3E%3C/svg%3E%0A");
3
+ background-repeat: no-repeat;
4
+ background-position: 10px center;
5
+ padding-left: 35px !important;
6
+ }
7
+
8
+ .form-floating {
9
+ input[type="search"] ~ label {
10
+ padding-left: 35px;
11
+ }
12
+ }
@@ -0,0 +1,11 @@
1
+ .form-label[required="required"], .form-check-label[required="required"] {
2
+ &:after {
3
+ content: ' *';
4
+ color: $danger;
5
+ }
6
+ }
7
+
8
+ @import "forms/autocomplete";
9
+ @import "forms/file";
10
+ @import "forms/repeater";
11
+ @import "forms/search";
@@ -1,3 +1,8 @@
1
1
  html {
2
2
  height: 100%;
3
+ }
4
+
5
+ mark {
6
+ padding: 0;
7
+ box-shadow: 0 $mark-padding 0 $mark-bg, 0 (-$mark-padding) 0 $mark-bg;
3
8
  }
@@ -18,7 +18,6 @@ $enable-shadows: true;
18
18
  // Typography
19
19
  $font-size-base: 0.9rem;
20
20
  $font-size-xs: $font-size-base * .75;
21
- $font-weight-medium: 500;
22
21
  $input-font-size: $font-size-base;
23
22
  $form-select-font-size: $font-size-base;
24
23
  $btn-font-size: $font-size-base;
@@ -29,7 +28,7 @@ $table-border-color: $gray-200;
29
28
  $table-group-separator-color: $gray-200;
30
29
  $table-hover-bg: $gray-100;
31
30
  $table-color: $gray-500;
32
- $table-th-font-weight: $font-weight-medium;
31
+ $table-th-font-weight: $headings-font-weight;
33
32
  $table-cell-padding-x: 1rem;
34
33
  $table-cell-padding-y: 0.8rem;
35
34
 
@@ -53,13 +52,16 @@ $btn-link-color: $gray-500;
53
52
  // Form
54
53
  $input-group-addon-bg: $gray-100;
55
54
 
55
+ // Mark
56
+ $mark-bg: #ffec89;
57
+
56
58
  // Utilities
57
59
  $custom-utilities: (
58
60
  "font-weight": (
59
61
  property: font-weight,
60
62
  class: fw,
61
63
  values: (
62
- medium: $font-weight-medium,
64
+ medium: $headings-font-weight,
63
65
  light: $font-weight-light,
64
66
  lighter: $font-weight-lighter,
65
67
  normal: $font-weight-normal,