headmin 0.4.1 → 0.5.1

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.lock-487e157d270f3062a98b7b2a012753708-1272821827 +0 -0
  3. data/CHANGELOG.md +16 -2
  4. data/Gemfile.lock +77 -79
  5. data/app/assets/javascripts/headmin/controllers/date_range_controller.js +12 -6
  6. data/app/assets/javascripts/headmin/controllers/filter_controller.js +61 -11
  7. data/app/assets/javascripts/headmin/controllers/filter_row_controller.js +50 -0
  8. data/app/assets/javascripts/headmin/controllers/flatpickr_controller.js +2 -6
  9. data/app/assets/javascripts/headmin/controllers/popup_controller.js +3 -1
  10. data/app/assets/javascripts/headmin/controllers/table_actions_controller.js +16 -21
  11. data/app/assets/javascripts/headmin/index.js +2 -0
  12. data/app/assets/javascripts/headmin.js +119 -38
  13. data/app/assets/stylesheets/headmin/filter.scss +74 -0
  14. data/app/assets/stylesheets/headmin/general.scss +0 -1
  15. data/app/assets/stylesheets/headmin/layout/body.scss +5 -0
  16. data/app/assets/stylesheets/headmin/popup.scss +0 -1
  17. data/app/assets/stylesheets/headmin/table.scss +7 -0
  18. data/app/assets/stylesheets/headmin.css +73 -2
  19. data/app/controllers/concerns/headmin/filterable.rb +27 -0
  20. data/app/models/concerns/headmin/field.rb +4 -2
  21. data/app/models/concerns/headmin/fieldable.rb +138 -44
  22. data/app/models/headmin/blocks_view.rb +1 -1
  23. data/app/models/headmin/filter/base.rb +238 -0
  24. data/app/models/headmin/filter/base_view.rb +64 -0
  25. data/app/models/headmin/filter/boolean.rb +15 -0
  26. data/app/models/headmin/filter/boolean_view.rb +61 -0
  27. data/app/models/headmin/filter/button_view.rb +25 -0
  28. data/app/models/headmin/filter/conditional_view.rb +16 -0
  29. data/app/models/headmin/filter/date.rb +19 -0
  30. data/app/models/headmin/filter/date_view.rb +52 -0
  31. data/app/models/headmin/filter/flatpickr_view.rb +54 -0
  32. data/app/models/headmin/filter/menu_item_view.rb +6 -0
  33. data/app/models/headmin/filter/money.rb +13 -0
  34. data/app/models/headmin/filter/number.rb +27 -0
  35. data/app/models/headmin/filter/number_view.rb +54 -0
  36. data/app/models/headmin/filter/operator_view.rb +30 -0
  37. data/app/models/headmin/filter/options_view.rb +61 -0
  38. data/app/models/headmin/filter/row_view.rb +13 -0
  39. data/app/models/headmin/filter/search.rb +18 -0
  40. data/app/models/headmin/filter/search_view.rb +31 -0
  41. data/app/models/headmin/filter/text.rb +25 -0
  42. data/app/models/headmin/filter/text_view.rb +53 -0
  43. data/app/models/headmin/filters.rb +29 -0
  44. data/app/models/headmin/form/color_view.rb +48 -0
  45. data/app/models/headmin/form/datetime_range_view.rb +25 -0
  46. data/app/models/headmin/form/datetime_view.rb +45 -0
  47. data/app/models/headmin/form/flatpickr_range_view.rb +4 -15
  48. data/app/models/headmin/form/flatpickr_view.rb +3 -12
  49. data/app/models/view_model.rb +1 -1
  50. data/app/views/examples/admin.html.erb +13 -13
  51. data/app/views/examples/auth.html.erb +1 -1
  52. data/app/views/headmin/_blocks.html.erb +1 -1
  53. data/app/views/headmin/_filters.html.erb +6 -6
  54. data/app/views/headmin/_form.html.erb +2 -2
  55. data/app/views/headmin/_index.html.erb +1 -1
  56. data/app/views/headmin/_pagination.html.erb +1 -1
  57. data/app/views/headmin/_popup.html.erb +2 -2
  58. data/app/views/headmin/_table.html.erb +1 -1
  59. data/app/views/headmin/dropdown/_devise.html.erb +8 -8
  60. data/app/views/headmin/dropdown/_locale.html.erb +4 -4
  61. data/app/views/headmin/filters/_base.html.erb +95 -0
  62. data/app/views/headmin/filters/_boolean.html.erb +23 -0
  63. data/app/views/headmin/filters/_date.html.erb +14 -38
  64. data/app/views/headmin/filters/_flatpickr.html.erb +15 -48
  65. data/app/views/headmin/filters/_number.html.erb +23 -0
  66. data/app/views/headmin/filters/_options.html.erb +24 -0
  67. data/app/views/headmin/filters/_search.html.erb +14 -12
  68. data/app/views/headmin/filters/_text.html.erb +23 -0
  69. data/app/views/headmin/filters/filter/_button.html.erb +9 -10
  70. data/app/views/headmin/filters/filter/_conditional.html.erb +18 -0
  71. data/app/views/headmin/filters/filter/_menu_item.html.erb +5 -2
  72. data/app/views/headmin/filters/filter/_null_select.html.erb +8 -0
  73. data/app/views/headmin/filters/filter/_operator.html.erb +16 -0
  74. data/app/views/headmin/filters/filter/_row.html.erb +11 -0
  75. data/app/views/headmin/forms/_blocks.html.erb +1 -1
  76. data/app/views/headmin/forms/_color.html.erb +32 -0
  77. data/app/views/headmin/forms/_date_range.html.erb +3 -3
  78. data/app/views/headmin/forms/_datetime.html.erb +41 -0
  79. data/app/views/headmin/forms/_datetime_range.html.erb +40 -0
  80. data/app/views/headmin/forms/_file.html.erb +3 -3
  81. data/app/views/headmin/forms/_flatpickr.html.erb +1 -1
  82. data/app/views/headmin/forms/_flatpickr_range.html.erb +3 -4
  83. data/app/views/headmin/forms/_label.html.erb +1 -1
  84. data/app/views/headmin/forms/_repeater.html.erb +12 -12
  85. data/app/views/headmin/forms/fields/_base.html.erb +1 -1
  86. data/app/views/headmin/forms/fields/_file.html.erb +3 -3
  87. data/app/views/headmin/forms/fields/_files.html.erb +17 -0
  88. data/app/views/headmin/forms/fields/_group.html.erb +5 -5
  89. data/app/views/headmin/forms/fields/_list.html.erb +4 -4
  90. data/app/views/headmin/forms/fields/_text.html.erb +2 -2
  91. data/app/views/headmin/layout/_footer.html.erb +1 -1
  92. data/app/views/headmin/layout/_main.html.erb +1 -1
  93. data/app/views/headmin/nav/_dropdown.html.erb +3 -3
  94. data/app/views/headmin/nav/_item.html.erb +2 -2
  95. data/app/views/headmin/nav/item/_devise.html.erb +8 -8
  96. data/app/views/headmin/nav/item/_locale.html.erb +4 -4
  97. data/app/views/headmin/table/_actions.html.erb +3 -6
  98. data/app/views/headmin/table/actions/_export.html.erb +1 -1
  99. data/app/views/headmin/table/body/_color.html.erb +10 -0
  100. data/app/views/headmin/table/body/_row.html.erb +3 -3
  101. data/app/views/headmin/table/foot/_id.html.erb +1 -1
  102. data/app/views/headmin/views/devise/confirmations/_new.html.erb +1 -1
  103. data/app/views/headmin/views/devise/passwords/_edit.html.erb +2 -2
  104. data/app/views/headmin/views/devise/passwords/_new.html.erb +1 -1
  105. data/app/views/headmin/views/devise/registrations/_edit.html.erb +4 -4
  106. data/app/views/headmin/views/devise/registrations/_new.html.erb +3 -3
  107. data/app/views/headmin/views/devise/sessions/_new.html.erb +3 -3
  108. data/app/views/headmin/views/devise/unlocks/_new.html.erb +1 -1
  109. data/config/locales/en.yml +4 -0
  110. data/config/locales/headmin/dropdown/en.yml +6 -0
  111. data/config/locales/headmin/dropdown/nl.yml +6 -0
  112. data/config/locales/headmin/filters/en.yml +26 -1
  113. data/config/locales/headmin/filters/nl.yml +26 -1
  114. data/config/locales/headmin/layout/en.yml +0 -9
  115. data/config/locales/headmin/layout/nl.yml +0 -9
  116. data/config/locales/headmin/nav/en.yml +7 -0
  117. data/config/locales/headmin/nav/nl.yml +7 -0
  118. data/config/locales/nl.yml +4 -0
  119. data/lib/generators/templates/views/layouts/auth.html.erb +1 -1
  120. data/lib/headmin/version.rb +1 -1
  121. data/package.json +1 -1
  122. metadata +47 -7
  123. data/app/controllers/concerns/headmin/filter.rb +0 -5
  124. data/app/controllers/concerns/headmin/searchable.rb +0 -15
  125. data/app/views/headmin/filters/_select.html.erb +0 -45
  126. data/app/views/headmin/filters/filter/_template.html.erb +0 -13
  127. data/app/views/headmin/forms/fields/_image.html.erb +0 -17
@@ -7457,9 +7457,6 @@ var blocks_controller_default = class extends Controller {
7457
7457
 
7458
7458
  // app/assets/javascripts/headmin/controllers/date_range_controller.js
7459
7459
  var date_range_controller_default = class extends Controller {
7460
- static get targets() {
7461
- return ["dateInput", "startDateInput", "endDateInput"];
7462
- }
7463
7460
  update(event) {
7464
7461
  const flatpickr2 = event.target._flatpickr;
7465
7462
  const startDate = flatpickr2.selectedDates[0];
@@ -7468,10 +7465,18 @@ var date_range_controller_default = class extends Controller {
7468
7465
  this.setEndDateInputValue(this.formatDate(endDate));
7469
7466
  }
7470
7467
  setStartDateInputValue(value) {
7471
- this.startDateInputTarget.value = value;
7468
+ const startDateInput = this.startDateInput();
7469
+ startDateInput.value = value;
7472
7470
  }
7473
7471
  setEndDateInputValue(value) {
7474
- this.endDateInputTarget.value = value;
7472
+ const endDateInput = this.endDateInput();
7473
+ endDateInput.value = value;
7474
+ }
7475
+ startDateInput() {
7476
+ return this.element.nextElementSibling;
7477
+ }
7478
+ endDateInput() {
7479
+ return this.startDateInput().nextElementSibling;
7475
7480
  }
7476
7481
  formatDate(date) {
7477
7482
  if (date instanceof Date) {
@@ -7704,18 +7709,16 @@ var file_preview_controller_default = class extends Controller {
7704
7709
  // app/assets/javascripts/headmin/controllers/filter_controller.js
7705
7710
  var filter_controller_default = class extends Controller {
7706
7711
  static get targets() {
7707
- return ["button", "popup"];
7712
+ return ["button", "popup", "conditional", "operator", "value", "hidden", "wrapper", "template", "row"];
7713
+ }
7714
+ static get values() {
7715
+ return {
7716
+ name: String
7717
+ };
7708
7718
  }
7709
7719
  connect() {
7710
7720
  this.element.controller = this;
7711
- document.addEventListener("click", (event) => {
7712
- this.handleOutsideClick(event);
7713
- });
7714
- }
7715
- handleOutsideClick(event) {
7716
- if (!this.isClickedInside(event)) {
7717
- this.close();
7718
- }
7721
+ this.updateHiddenValue();
7719
7722
  }
7720
7723
  toggle(event) {
7721
7724
  const expanded = this.buttonTarget.getAttribute("aria-expanded") === "true";
@@ -7733,6 +7736,29 @@ var filter_controller_default = class extends Controller {
7733
7736
  this.buttonTarget.setAttribute("aria-expanded", "false");
7734
7737
  this.popupTarget.classList.add("closed");
7735
7738
  }
7739
+ add(event) {
7740
+ event.preventDefault();
7741
+ const html = this.getTemplateHTML();
7742
+ this.wrapperTarget.insertAdjacentHTML("beforeend", html);
7743
+ }
7744
+ remove(event) {
7745
+ event.preventDefault();
7746
+ const inputGroup = event.currentTarget.closest('[data-filter-target="row"]');
7747
+ const conditional = inputGroup.previousElementSibling != null ? inputGroup.previousElementSibling : inputGroup.nextElementSibling;
7748
+ inputGroup.remove();
7749
+ if (conditional != null)
7750
+ conditional.remove();
7751
+ this.updateHiddenValue();
7752
+ if (this.valueTargets.length === 0) {
7753
+ this.removeFilter();
7754
+ }
7755
+ }
7756
+ removeFilter() {
7757
+ const form = this.buttonTarget.closest("form");
7758
+ this.buttonTarget.remove();
7759
+ this.popupTarget.remove();
7760
+ form.submit();
7761
+ }
7736
7762
  isClickedInside(event) {
7737
7763
  if (!event) {
7738
7764
  return false;
@@ -7742,6 +7768,65 @@ var filter_controller_default = class extends Controller {
7742
7768
  const inAddButton = event.target.dataset.action === "click->filters#add";
7743
7769
  return inPopup || inButton || inAddButton;
7744
7770
  }
7771
+ updateHiddenValue() {
7772
+ this.hiddenTarget.value = this.buildInstructionString();
7773
+ }
7774
+ buildInstructionString() {
7775
+ let string = "";
7776
+ for (const row of this.rowTargets) {
7777
+ const conditional = row.previousElementSibling ? row.previousElementSibling.querySelector('[data-filter-target="conditional"]').value : null;
7778
+ const operator = row.querySelector('[data-filter-target="operator"]').value;
7779
+ const value = row.querySelector('[data-filter-target="value"]').value;
7780
+ string += `${conditional || ""}${operator}:${value}`;
7781
+ }
7782
+ return string;
7783
+ }
7784
+ getTemplateHTML() {
7785
+ const template = this.templateTarget;
7786
+ return template.innerHTML;
7787
+ }
7788
+ };
7789
+
7790
+ // app/assets/javascripts/headmin/controllers/filter_row_controller.js
7791
+ var filter_row_controller_default = class extends Controller {
7792
+ static get targets() {
7793
+ return ["original", "operator", "null"];
7794
+ }
7795
+ connect() {
7796
+ this.operatorTarget.addEventListener("change", () => this.handleOperatorChange());
7797
+ this.handleOperatorChange();
7798
+ }
7799
+ handleOperatorChange() {
7800
+ if (this.operatorTarget.value === "is_null" || this.operatorTarget.value === "is_not_null") {
7801
+ this.toggleNullInput();
7802
+ } else {
7803
+ this.toggleOriginalInput();
7804
+ }
7805
+ }
7806
+ toggleNullInput() {
7807
+ this.hideOriginal();
7808
+ this.showNull();
7809
+ }
7810
+ toggleOriginalInput() {
7811
+ this.showOriginal();
7812
+ this.hideNull();
7813
+ }
7814
+ hideOriginal() {
7815
+ this.originalTarget.style.display = "none";
7816
+ this.originalTarget.setAttribute("data-filter-target", "value_original");
7817
+ }
7818
+ showOriginal() {
7819
+ this.originalTarget.style.display = "block";
7820
+ this.originalTarget.setAttribute("data-filter-target", "value");
7821
+ }
7822
+ hideNull() {
7823
+ this.nullTarget.style.display = "none";
7824
+ this.nullTarget.setAttribute("data-filter-target", "value_null");
7825
+ }
7826
+ showNull() {
7827
+ this.nullTarget.style.display = "block";
7828
+ this.nullTarget.setAttribute("data-filter-target", "value");
7829
+ }
7745
7830
  };
7746
7831
 
7747
7832
  // app/assets/javascripts/headmin/controllers/filters_controller.js
@@ -9975,15 +10060,12 @@ var i18n_default = class {
9975
10060
 
9976
10061
  // app/assets/javascripts/headmin/controllers/flatpickr_controller.js
9977
10062
  var flatpickr_controller_default = class extends Controller {
9978
- static get targets() {
9979
- return ["input"];
9980
- }
9981
10063
  connect() {
9982
10064
  const options = { ...this.defaultOptions(), ...this.options() };
9983
- esm_default(this.inputTarget, options);
10065
+ esm_default(this.element, options);
9984
10066
  }
9985
10067
  options() {
9986
- return JSON.parse(this.inputTarget.getAttribute("data-flatpickr"));
10068
+ return JSON.parse(this.element.getAttribute("data-flatpickr"));
9987
10069
  }
9988
10070
  defaultOptions() {
9989
10071
  return {
@@ -15081,6 +15163,9 @@ var popup_controller_default = class extends Controller {
15081
15163
  });
15082
15164
  }
15083
15165
  handleOutsideClick(event) {
15166
+ const itemRemoved = !document.body.contains(event.target);
15167
+ if (itemRemoved)
15168
+ return;
15084
15169
  const inPopup = event.target.closest('[data-popup-target="popup"]') !== null;
15085
15170
  const inButton = event.target.closest('[data-popup-target="button"]') !== null;
15086
15171
  const openPopup = document.querySelector('[data-popup-target="popup"]:not(.closed)');
@@ -15305,11 +15390,11 @@ var select_controller_default = class extends Controller {
15305
15390
  // app/assets/javascripts/headmin/controllers/table_actions_controller.js
15306
15391
  var table_actions_controller_default = class extends Controller {
15307
15392
  static get targets() {
15308
- return ["wrapper", "form", "select", "method", "button", "idInputTemplate", "id", "counter"];
15393
+ return ["wrapper", "form", "select", "method", "button", "idInput", "counter"];
15309
15394
  }
15310
15395
  connect() {
15311
15396
  this.wrapperTarget.addEventListener("idSelectionChanged", (event) => {
15312
- this.updateIdFields(event.detail.ids);
15397
+ this.updateIdInput(event.detail.ids);
15313
15398
  this.updateCounter(event.detail.count);
15314
15399
  this.toggleCounter(event.detail.count);
15315
15400
  });
@@ -15321,16 +15406,21 @@ var table_actions_controller_default = class extends Controller {
15321
15406
  this.updateFormDataAttributes();
15322
15407
  this.enableButton();
15323
15408
  }
15324
- updateIdFields(ids) {
15325
- this.removeIds();
15326
- if (ids instanceof Array) {
15327
- ids.forEach((id) => {
15328
- this.addId(id);
15329
- });
15409
+ updateIdInput(ids) {
15410
+ if (ids == null) {
15411
+ this.disableIdInput();
15412
+ this.idInputTarget.value = "";
15330
15413
  } else {
15331
- this.addId("");
15414
+ this.enableIdInput();
15415
+ this.idInputTarget.value = `in:${ids.join(",")}`;
15332
15416
  }
15333
15417
  }
15418
+ disableIdInput() {
15419
+ this.idInputTarget.removeAttribute("name");
15420
+ }
15421
+ enableIdInput() {
15422
+ this.idInputTarget.setAttribute("name", "id");
15423
+ }
15334
15424
  updateCounter(count) {
15335
15425
  let htmlString = "";
15336
15426
  switch (count) {
@@ -15385,16 +15475,6 @@ var table_actions_controller_default = class extends Controller {
15385
15475
  enableButton() {
15386
15476
  this.buttonTarget.removeAttribute("disabled");
15387
15477
  }
15388
- addId(id) {
15389
- const template = this.idInputTemplateTarget;
15390
- const input = template.innerHTML.replace(/ID/g, id);
15391
- this.formTarget.insertAdjacentHTML("afterbegin", input);
15392
- }
15393
- removeIds() {
15394
- this.idTargets.forEach((input) => {
15395
- this.formTarget.removeChild(input);
15396
- });
15397
- }
15398
15478
  };
15399
15479
 
15400
15480
  // app/assets/javascripts/headmin/controllers/table_controller.js
@@ -15510,6 +15590,7 @@ var Headmin = class {
15510
15590
  Stimulus.register("dropzone", dropzone_controller_default);
15511
15591
  Stimulus.register("file-preview", file_preview_controller_default);
15512
15592
  Stimulus.register("filter", filter_controller_default);
15593
+ Stimulus.register("filter-row", filter_row_controller_default);
15513
15594
  Stimulus.register("filters", filters_controller_default);
15514
15595
  Stimulus.register("flatpickr", flatpickr_controller_default);
15515
15596
  Stimulus.register("hello", hello_controller_default);
@@ -23,11 +23,85 @@
23
23
  }
24
24
  }
25
25
 
26
+ .h-filter-row {
27
+ padding: 0.5rem 0 0.5rem 0;
28
+ position: relative;
29
+ flex-wrap: unset;
30
+ display: flex;
31
+ gap: 10px;
32
+
33
+ &:first-child {
34
+ padding-top: 0
35
+ }
36
+
37
+ &:hover {
38
+ .h-filter-add-input, .h-filter-remove-input {
39
+ display: block;
40
+ }
41
+ }
42
+
43
+ input, select:not(.h-filter-operator) {
44
+ min-width: 200px;
45
+ }
46
+ }
47
+
48
+ .h-filter-operator {
49
+ width: 62px;
50
+ }
51
+
52
+ .h-filter-conditional {
53
+ position: relative;
54
+ display: flex;
55
+ justify-content: center;
56
+ align-items: center;
57
+
58
+ select {
59
+ width: 50px;
60
+ }
61
+
62
+ &::before {
63
+ content: '';
64
+ position: absolute;
65
+ top: calc(50% - 1px);
66
+ left: 0;
67
+ width: calc(50% - 35px);
68
+ height: 1px;
69
+ background: $popover-border-color;
70
+ }
71
+
72
+ &::after {
73
+ content: '';
74
+ position: absolute;
75
+ top: calc(50% - 1px);
76
+ left: calc(50% + 35px);
77
+ width: calc(50% - 35px);
78
+ height: 1px;
79
+ background: $popover-border-color;
80
+ }
81
+ }
82
+
26
83
  .h-filter-remove {
27
84
  border-left: 1px solid $gray-300;
28
85
  padding-left: 8px;
29
86
  margin-left: 2px;
87
+
30
88
  i::before {
31
89
  font-size: 0.8em;
32
90
  }
91
+ }
92
+
93
+ .h-filter-add-input {
94
+ position: absolute !important;
95
+ top: calc(100% - 12px);
96
+ left: calc(50% - 17px);
97
+ z-index: 9;
98
+ display: none;
99
+ }
100
+
101
+ .h-filter-remove-input {
102
+ position: absolute !important;
103
+ top: calc(50% - 15px);
104
+ left: calc(100% - 4px);
105
+ z-index: 9;
106
+ display: none;
33
107
  }
@@ -1,6 +1,5 @@
1
1
  html {
2
2
  height: 100%;
3
- background: red;
4
3
  }
5
4
 
6
5
  .list-group-item {
@@ -2,5 +2,10 @@
2
2
  background: $card-cap-bg;
3
3
  height: 100vh;
4
4
  overflow-y: scroll;
5
+ display: flex;
6
+ flex-direction: column;
5
7
  }
6
8
 
9
+ .content {
10
+ flex-grow: 1
11
+ }
@@ -2,7 +2,6 @@
2
2
  padding: 10px;
3
3
  background: $white;
4
4
  z-index: $zindex-popover;
5
- width: $popover-max-width;
6
5
  @include reset-text();
7
6
  @include font-size($popover-font-size);
8
7
  word-wrap: break-word;
@@ -34,3 +34,10 @@
34
34
  max-width: 100%;
35
35
  }
36
36
  }
37
+
38
+ .h-table-cell-color {
39
+ width: 22px;
40
+ height: 22px;
41
+ border-radius: 3px;
42
+ background-color: var(--cell-color);
43
+ }
@@ -13103,7 +13103,6 @@ input[type=search] {
13103
13103
  }
13104
13104
  html {
13105
13105
  height: 100%;
13106
- background: red;
13107
13106
  }
13108
13107
  .list-group-item.active .text-muted {
13109
13108
  color: #fff !important;
@@ -13117,6 +13116,11 @@ mark,
13117
13116
  background: rgba(0, 0, 0, 0.03);
13118
13117
  height: 100vh;
13119
13118
  overflow-y: scroll;
13119
+ display: flex;
13120
+ flex-direction: column;
13121
+ }
13122
+ .content {
13123
+ flex-grow: 1;
13120
13124
  }
13121
13125
  .sidebar {
13122
13126
  font-size: 0.99rem;
@@ -13200,6 +13204,12 @@ body.empty {
13200
13204
  width: 300px;
13201
13205
  max-width: 100%;
13202
13206
  }
13207
+ .h-table-cell-color {
13208
+ width: 22px;
13209
+ height: 22px;
13210
+ border-radius: 3px;
13211
+ background-color: var(--cell-color);
13212
+ }
13203
13213
  .btn-link {
13204
13214
  text-decoration: none;
13205
13215
  }
@@ -13310,6 +13320,54 @@ body.empty {
13310
13320
  .h-filter-popup.closed {
13311
13321
  display: none;
13312
13322
  }
13323
+ .h-filter-row {
13324
+ padding: 0.5rem 0 0.5rem 0;
13325
+ position: relative;
13326
+ flex-wrap: unset;
13327
+ display: flex;
13328
+ gap: 10px;
13329
+ }
13330
+ .h-filter-row:first-child {
13331
+ padding-top: 0;
13332
+ }
13333
+ .h-filter-row:hover .h-filter-add-input,
13334
+ .h-filter-row:hover .h-filter-remove-input {
13335
+ display: block;
13336
+ }
13337
+ .h-filter-row input,
13338
+ .h-filter-row select:not(.h-filter-operator) {
13339
+ min-width: 200px;
13340
+ }
13341
+ .h-filter-operator {
13342
+ width: 62px;
13343
+ }
13344
+ .h-filter-conditional {
13345
+ position: relative;
13346
+ display: flex;
13347
+ justify-content: center;
13348
+ align-items: center;
13349
+ }
13350
+ .h-filter-conditional select {
13351
+ width: 50px;
13352
+ }
13353
+ .h-filter-conditional::before {
13354
+ content: "";
13355
+ position: absolute;
13356
+ top: calc(50% - 1px);
13357
+ left: 0;
13358
+ width: calc(50% - 35px);
13359
+ height: 1px;
13360
+ background: rgba(0, 0, 0, 0.2);
13361
+ }
13362
+ .h-filter-conditional::after {
13363
+ content: "";
13364
+ position: absolute;
13365
+ top: calc(50% - 1px);
13366
+ left: calc(50% + 35px);
13367
+ width: calc(50% - 35px);
13368
+ height: 1px;
13369
+ background: rgba(0, 0, 0, 0.2);
13370
+ }
13313
13371
  .h-filter-remove {
13314
13372
  border-left: 1px solid #d1d5db;
13315
13373
  padding-left: 8px;
@@ -13318,11 +13376,24 @@ body.empty {
13318
13376
  .h-filter-remove i::before {
13319
13377
  font-size: 0.8em;
13320
13378
  }
13379
+ .h-filter-add-input {
13380
+ position: absolute !important;
13381
+ top: calc(100% - 12px);
13382
+ left: calc(50% - 17px);
13383
+ z-index: 9;
13384
+ display: none;
13385
+ }
13386
+ .h-filter-remove-input {
13387
+ position: absolute !important;
13388
+ top: calc(50% - 15px);
13389
+ left: calc(100% - 4px);
13390
+ z-index: 9;
13391
+ display: none;
13392
+ }
13321
13393
  .h-popup {
13322
13394
  padding: 10px;
13323
13395
  background: #ffffff;
13324
13396
  z-index: 1070;
13325
- width: 276px;
13326
13397
  font-family: var(--bs-font-sans-serif);
13327
13398
  font-style: normal;
13328
13399
  font-weight: 400;
@@ -0,0 +1,27 @@
1
+ module Headmin
2
+ module Filterable
3
+ # Will create a Headmin::Filters object with a default configuration for "id" and "search"
4
+ #
5
+ # Example:
6
+ #
7
+ # orders = Order
8
+ # orders = filter(orders, {
9
+ # status: :text,
10
+ # price: :number,
11
+ # in_stock: :boolean
12
+ # })
13
+ # @orders = orders.all
14
+
15
+ def filter(collection, filter_types = {})
16
+ type_hash = default_filter_types.merge(filter_types)
17
+ Headmin::Filters.new(params, type_hash).query(collection)
18
+ end
19
+
20
+ def default_filter_types
21
+ {
22
+ id: :number,
23
+ search: :search
24
+ }
25
+ end
26
+ end
27
+ end
@@ -11,8 +11,10 @@ module Headmin
11
11
  belongs_to :field, optional: true, touch: true
12
12
  has_many :fields, foreign_key: "parent_id"
13
13
  accepts_nested_attributes_for :fields, allow_destroy: true
14
- has_one_attached :file
15
- accepts_nested_attributes_for :file_attachment
14
+
15
+ # field_type: :files, :file
16
+ has_many_attached :files
17
+ accepts_nested_attributes_for :files_attachments, allow_destroy: true
16
18
  end
17
19
  end
18
20
  end