headmin 0.4.0 → 0.5.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 (143) 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/autocomplete_controller.js +84 -21
  6. data/app/assets/javascripts/headmin/controllers/date_range_controller.js +12 -6
  7. data/app/assets/javascripts/headmin/controllers/filter_controller.js +61 -11
  8. data/app/assets/javascripts/headmin/controllers/filter_row_controller.js +50 -0
  9. data/app/assets/javascripts/headmin/controllers/flatpickr_controller.js +2 -6
  10. data/app/assets/javascripts/headmin/controllers/popup_controller.js +14 -5
  11. data/app/assets/javascripts/headmin/controllers/table_actions_controller.js +16 -21
  12. data/app/assets/javascripts/headmin/index.js +2 -0
  13. data/app/assets/javascripts/headmin.js +186 -56
  14. data/app/assets/stylesheets/headmin/filter.scss +74 -0
  15. data/app/assets/stylesheets/headmin/general.scss +8 -0
  16. data/app/assets/stylesheets/headmin/layout/body.scss +5 -0
  17. data/app/assets/stylesheets/headmin/popup.scss +0 -1
  18. data/app/assets/stylesheets/headmin.css +70 -1
  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/filter/base.rb +238 -0
  23. data/app/models/headmin/filter/base_view.rb +64 -0
  24. data/app/models/headmin/filter/boolean.rb +15 -0
  25. data/app/models/headmin/filter/boolean_view.rb +61 -0
  26. data/app/models/headmin/filter/button_view.rb +25 -0
  27. data/app/models/headmin/filter/conditional_view.rb +16 -0
  28. data/app/models/headmin/filter/date.rb +19 -0
  29. data/app/models/headmin/filter/date_view.rb +52 -0
  30. data/app/models/headmin/filter/flatpickr_view.rb +54 -0
  31. data/app/models/headmin/filter/menu_item_view.rb +6 -0
  32. data/app/models/headmin/filter/money.rb +13 -0
  33. data/app/models/headmin/filter/number.rb +27 -0
  34. data/app/models/headmin/filter/number_view.rb +54 -0
  35. data/app/models/headmin/filter/operator_view.rb +30 -0
  36. data/app/models/headmin/filter/options_view.rb +61 -0
  37. data/app/models/headmin/filter/row_view.rb +13 -0
  38. data/app/models/headmin/filter/search.rb +18 -0
  39. data/app/models/headmin/filter/search_view.rb +31 -0
  40. data/app/models/headmin/filter/text.rb +25 -0
  41. data/app/models/headmin/filter/text_view.rb +53 -0
  42. data/app/models/headmin/filters.rb +29 -0
  43. data/app/models/headmin/form/blocks_view.rb +1 -1
  44. data/app/models/headmin/form/checkbox_view.rb +3 -3
  45. data/app/models/headmin/form/date_range_view.rb +2 -2
  46. data/app/models/headmin/form/date_view.rb +5 -5
  47. data/app/models/headmin/form/datetime_range_view.rb +25 -0
  48. data/app/models/headmin/form/datetime_view.rb +45 -0
  49. data/app/models/headmin/form/email_view.rb +7 -7
  50. data/app/models/headmin/form/file_view.rb +6 -6
  51. data/app/models/headmin/form/flatpickr_range_view.rb +11 -22
  52. data/app/models/headmin/form/flatpickr_view.rb +4 -13
  53. data/app/models/headmin/form/input_group_view.rb +1 -1
  54. data/app/models/headmin/form/label_view.rb +1 -1
  55. data/app/models/headmin/form/number_view.rb +5 -5
  56. data/app/models/headmin/form/password_view.rb +5 -5
  57. data/app/models/headmin/form/redactorx_view.rb +2 -2
  58. data/app/models/headmin/form/search_view.rb +7 -7
  59. data/app/models/headmin/form/select_view.rb +6 -6
  60. data/app/models/headmin/form/switch_view.rb +1 -1
  61. data/app/models/headmin/form/text_view.rb +7 -7
  62. data/app/models/headmin/form/textarea_view.rb +5 -5
  63. data/app/models/headmin/form/url_view.rb +7 -7
  64. data/app/models/headmin/form/wrapper_view.rb +1 -1
  65. data/app/models/headmin/form/wysiwyg_view.rb +1 -1
  66. data/app/models/view_model.rb +1 -1
  67. data/app/views/examples/admin.html.erb +13 -13
  68. data/app/views/examples/auth.html.erb +1 -1
  69. data/app/views/headmin/_filters.html.erb +6 -6
  70. data/app/views/headmin/_form.html.erb +2 -2
  71. data/app/views/headmin/_index.html.erb +1 -1
  72. data/app/views/headmin/_pagination.html.erb +1 -1
  73. data/app/views/headmin/_popup.html.erb +2 -2
  74. data/app/views/headmin/_table.html.erb +1 -1
  75. data/app/views/headmin/dropdown/_devise.html.erb +8 -8
  76. data/app/views/headmin/dropdown/_locale.html.erb +4 -4
  77. data/app/views/headmin/filters/_base.html.erb +95 -0
  78. data/app/views/headmin/filters/_boolean.html.erb +23 -0
  79. data/app/views/headmin/filters/_date.html.erb +14 -38
  80. data/app/views/headmin/filters/_flatpickr.html.erb +15 -48
  81. data/app/views/headmin/filters/_number.html.erb +23 -0
  82. data/app/views/headmin/filters/_options.html.erb +24 -0
  83. data/app/views/headmin/filters/_search.html.erb +14 -12
  84. data/app/views/headmin/filters/_text.html.erb +23 -0
  85. data/app/views/headmin/filters/filter/_button.html.erb +9 -10
  86. data/app/views/headmin/filters/filter/_conditional.html.erb +18 -0
  87. data/app/views/headmin/filters/filter/_menu_item.html.erb +5 -2
  88. data/app/views/headmin/filters/filter/_null_select.html.erb +8 -0
  89. data/app/views/headmin/filters/filter/_operator.html.erb +16 -0
  90. data/app/views/headmin/filters/filter/_row.html.erb +11 -0
  91. data/app/views/headmin/forms/_blocks.html.erb +1 -1
  92. data/app/views/headmin/forms/_date_range.html.erb +3 -3
  93. data/app/views/headmin/forms/_datetime.html.erb +41 -0
  94. data/app/views/headmin/forms/_datetime_range.html.erb +40 -0
  95. data/app/views/headmin/forms/_file.html.erb +3 -3
  96. data/app/views/headmin/forms/_flatpickr.html.erb +1 -1
  97. data/app/views/headmin/forms/_flatpickr_range.html.erb +3 -4
  98. data/app/views/headmin/forms/_label.html.erb +1 -1
  99. data/app/views/headmin/forms/_repeater.html.erb +12 -12
  100. data/app/views/headmin/forms/fields/_base.html.erb +1 -1
  101. data/app/views/headmin/forms/fields/_file.html.erb +3 -3
  102. data/app/views/headmin/forms/fields/_files.html.erb +17 -0
  103. data/app/views/headmin/forms/fields/_group.html.erb +10 -5
  104. data/app/views/headmin/forms/fields/_list.html.erb +4 -4
  105. data/app/views/headmin/forms/fields/_text.html.erb +2 -2
  106. data/app/views/headmin/layout/_footer.html.erb +1 -1
  107. data/app/views/headmin/layout/_main.html.erb +1 -1
  108. data/app/views/headmin/nav/_dropdown.html.erb +3 -3
  109. data/app/views/headmin/nav/_item.html.erb +2 -2
  110. data/app/views/headmin/nav/item/_devise.html.erb +8 -8
  111. data/app/views/headmin/nav/item/_locale.html.erb +4 -4
  112. data/app/views/headmin/table/_actions.html.erb +3 -6
  113. data/app/views/headmin/table/actions/_export.html.erb +1 -1
  114. data/app/views/headmin/table/body/_row.html.erb +3 -3
  115. data/app/views/headmin/table/foot/_id.html.erb +1 -1
  116. data/app/views/headmin/views/devise/confirmations/_new.html.erb +1 -1
  117. data/app/views/headmin/views/devise/passwords/_edit.html.erb +2 -2
  118. data/app/views/headmin/views/devise/passwords/_new.html.erb +1 -1
  119. data/app/views/headmin/views/devise/registrations/_edit.html.erb +4 -4
  120. data/app/views/headmin/views/devise/registrations/_new.html.erb +3 -3
  121. data/app/views/headmin/views/devise/sessions/_new.html.erb +3 -3
  122. data/app/views/headmin/views/devise/unlocks/_new.html.erb +1 -1
  123. data/config/locales/en.yml +4 -0
  124. data/config/locales/headmin/dropdown/en.yml +6 -0
  125. data/config/locales/headmin/dropdown/nl.yml +6 -0
  126. data/config/locales/headmin/filters/en.yml +26 -1
  127. data/config/locales/headmin/filters/nl.yml +26 -1
  128. data/config/locales/headmin/forms/en.yml +1 -1
  129. data/config/locales/headmin/forms/nl.yml +1 -1
  130. data/config/locales/headmin/layout/en.yml +0 -9
  131. data/config/locales/headmin/layout/nl.yml +0 -9
  132. data/config/locales/headmin/nav/en.yml +7 -0
  133. data/config/locales/headmin/nav/nl.yml +7 -0
  134. data/config/locales/nl.yml +4 -0
  135. data/lib/generators/templates/views/layouts/auth.html.erb +1 -1
  136. data/lib/headmin/version.rb +1 -1
  137. data/package.json +1 -1
  138. metadata +44 -7
  139. data/app/controllers/concerns/headmin/filter.rb +0 -5
  140. data/app/controllers/concerns/headmin/searchable.rb +0 -15
  141. data/app/views/headmin/filters/_select.html.erb +0 -45
  142. data/app/views/headmin/filters/filter/_template.html.erb +0 -13
  143. data/app/views/headmin/forms/fields/_image.html.erb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b427a7de1334a6a27a66b145247d4d09379f25c446e01ff87bb17e46fb6098c6
4
- data.tar.gz: bda5b5c568a4872ce1be8e2da62a10d801370967c5beffed0ff27eb12576d407
3
+ metadata.gz: b4995d965477ea5691025ac2ca92fb211c13acc44f1068f402e013c1ec47cb08
4
+ data.tar.gz: dc1f5842c477771e2b9b9890e8b99e03af6c96daf6451ce8a09628c0dded3f3a
5
5
  SHA512:
6
- metadata.gz: 8b0363d0ee6aec32484806ef1e655d681127ae1318f0e391585f81d97b7cce1f28af1368c9038570a28aa0333abfafbc59dbcc65de49862f8248ff2a574edd83
7
- data.tar.gz: 4aaaff3a6a1550829bd10e4af436fc6a5bc0515ff6098b1b6761e577365b5816c0bbea2d65e3c2bee1633944a10890079cdb34a263998524e1adaf45e5887df0
6
+ metadata.gz: 64e5f636b7551f1348e640a2dc86e2de7169aee2b6decbda14fa9da3ceea4cbff594686fc27ed4c3a59de554491498c2f6fe60feda94f318bafdef5f8066e75e
7
+ data.tar.gz: 05bd1380da40f154c741f1a42be39a466a1f4c4f4860b1e9e1176024e24e61d9d68940b3d62932cb9f0413561e7425842f3d2f523a76e66d4b73cc2eef701369
data/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  # Headmin Changelog
2
2
 
3
- ## HEAD
3
+ ## 0.5
4
+ - FEATURE: Filters now have support for operators and conditionals
5
+ - BREAK: `headmin/filters` now return a form object that is required for each individual form
6
+ - DEPRECATED: `Headmin::Searchable` is replaced with `Headmin::Filterable`.
7
+ - A `filter(params, types)` method is available and accepts an optional types argument.
8
+ - The `search()` method is now deprecated and no longer required
9
+
10
+ ## 0.4.2
11
+ - FEATURE: New field type "files" to allow simple gallery creation
12
+ - FIX: Fields hash with duplicate keys no longer mix up
13
+ - BREAK: Image field is now deprecated. Use the file field instead
14
+ - BREAK: Field uses "has_many_attached :files" instead of "has_one_attache :file" now. Rename "file" to "files" in `active_storage_attachments`
15
+ - BREAK: You need to set a File object in the fields_hash, instead of creating an intermediate Blob instance
16
+
17
+ ## 0.4
4
18
  - FEATURE: Introduction of a `ViewModel` class to clean up the component views. This allows for a cleaner abstraction and better testing.
5
19
  - FEATURE: New `wysiwyg` component which currently defaults to Redactor X. Redactor X will be swapped with a free open source version in the 1.0 release.
6
20
  - FEATURE: `headmin/forms/redactorx` now accepts `hybrid: true` to hide the controls
@@ -15,7 +29,7 @@
15
29
  - BREAK: `headmin/forms/actions` and all of its child components have been removed
16
30
  - BREAK: `$font-weight-medium` has been removed from headmin overrides variables. We don't want to introduce new variable names that look like they are part of Bootstrap.
17
31
  - BREAK: `headmin/forms/date_range` has a different way of setting attribute specific values. We now have a `:start` and `:end` attribute that accept all possible input options for their respective inputs. Both have a required `:attribute` parameter now.
18
-
32
+ - BREAK: filter concern (controllers) has been renamed to filterable
19
33
  ```erb
20
34
  # Old
21
35
  <%= render "headmin/forms/date_range", form: form, start_attribute: :start_date, end_attribute: :end_date %>
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- headmin (0.3.4)
4
+ headmin (0.4.2)
5
5
  closure_tree (~> 7.4)
6
6
  inline_svg (~> 1.7)
7
7
  redcarpet (~> 3.5)
@@ -10,67 +10,67 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- actioncable (7.0.1)
14
- actionpack (= 7.0.1)
15
- activesupport (= 7.0.1)
13
+ actioncable (7.0.2.4)
14
+ actionpack (= 7.0.2.4)
15
+ activesupport (= 7.0.2.4)
16
16
  nio4r (~> 2.0)
17
17
  websocket-driver (>= 0.6.1)
18
- actionmailbox (7.0.1)
19
- actionpack (= 7.0.1)
20
- activejob (= 7.0.1)
21
- activerecord (= 7.0.1)
22
- activestorage (= 7.0.1)
23
- activesupport (= 7.0.1)
18
+ actionmailbox (7.0.2.4)
19
+ actionpack (= 7.0.2.4)
20
+ activejob (= 7.0.2.4)
21
+ activerecord (= 7.0.2.4)
22
+ activestorage (= 7.0.2.4)
23
+ activesupport (= 7.0.2.4)
24
24
  mail (>= 2.7.1)
25
25
  net-imap
26
26
  net-pop
27
27
  net-smtp
28
- actionmailer (7.0.1)
29
- actionpack (= 7.0.1)
30
- actionview (= 7.0.1)
31
- activejob (= 7.0.1)
32
- activesupport (= 7.0.1)
28
+ actionmailer (7.0.2.4)
29
+ actionpack (= 7.0.2.4)
30
+ actionview (= 7.0.2.4)
31
+ activejob (= 7.0.2.4)
32
+ activesupport (= 7.0.2.4)
33
33
  mail (~> 2.5, >= 2.5.4)
34
34
  net-imap
35
35
  net-pop
36
36
  net-smtp
37
37
  rails-dom-testing (~> 2.0)
38
- actionpack (7.0.1)
39
- actionview (= 7.0.1)
40
- activesupport (= 7.0.1)
38
+ actionpack (7.0.2.4)
39
+ actionview (= 7.0.2.4)
40
+ activesupport (= 7.0.2.4)
41
41
  rack (~> 2.0, >= 2.2.0)
42
42
  rack-test (>= 0.6.3)
43
43
  rails-dom-testing (~> 2.0)
44
44
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
45
- actiontext (7.0.1)
46
- actionpack (= 7.0.1)
47
- activerecord (= 7.0.1)
48
- activestorage (= 7.0.1)
49
- activesupport (= 7.0.1)
45
+ actiontext (7.0.2.4)
46
+ actionpack (= 7.0.2.4)
47
+ activerecord (= 7.0.2.4)
48
+ activestorage (= 7.0.2.4)
49
+ activesupport (= 7.0.2.4)
50
50
  globalid (>= 0.6.0)
51
51
  nokogiri (>= 1.8.5)
52
- actionview (7.0.1)
53
- activesupport (= 7.0.1)
52
+ actionview (7.0.2.4)
53
+ activesupport (= 7.0.2.4)
54
54
  builder (~> 3.1)
55
55
  erubi (~> 1.4)
56
56
  rails-dom-testing (~> 2.0)
57
57
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
58
- activejob (7.0.1)
59
- activesupport (= 7.0.1)
58
+ activejob (7.0.2.4)
59
+ activesupport (= 7.0.2.4)
60
60
  globalid (>= 0.3.6)
61
- activemodel (7.0.1)
62
- activesupport (= 7.0.1)
63
- activerecord (7.0.1)
64
- activemodel (= 7.0.1)
65
- activesupport (= 7.0.1)
66
- activestorage (7.0.1)
67
- actionpack (= 7.0.1)
68
- activejob (= 7.0.1)
69
- activerecord (= 7.0.1)
70
- activesupport (= 7.0.1)
61
+ activemodel (7.0.2.4)
62
+ activesupport (= 7.0.2.4)
63
+ activerecord (7.0.2.4)
64
+ activemodel (= 7.0.2.4)
65
+ activesupport (= 7.0.2.4)
66
+ activestorage (7.0.2.4)
67
+ actionpack (= 7.0.2.4)
68
+ activejob (= 7.0.2.4)
69
+ activerecord (= 7.0.2.4)
70
+ activesupport (= 7.0.2.4)
71
71
  marcel (~> 1.0)
72
72
  mini_mime (>= 1.1.0)
73
- activesupport (7.0.1)
73
+ activesupport (7.0.2.4)
74
74
  concurrent-ruby (~> 1.0, >= 1.0.2)
75
75
  i18n (>= 1.6, < 2)
76
76
  minitest (>= 5.1)
@@ -91,25 +91,24 @@ GEM
91
91
  closure_tree (7.4.0)
92
92
  activerecord (>= 4.2.10)
93
93
  with_advisory_lock (>= 4.0.0)
94
- concurrent-ruby (1.1.9)
94
+ concurrent-ruby (1.1.10)
95
95
  crass (1.0.6)
96
- debug (1.4.0)
96
+ debug (1.5.0)
97
97
  irb (>= 1.3.6)
98
98
  reline (>= 0.2.7)
99
99
  digest (3.1.0)
100
100
  erubi (1.10.0)
101
101
  globalid (1.0.0)
102
102
  activesupport (>= 5.0)
103
- i18n (1.9.1)
103
+ i18n (1.10.0)
104
104
  concurrent-ruby (~> 1.0)
105
105
  inline_svg (1.8.0)
106
106
  activesupport (>= 3.0)
107
107
  nokogiri (>= 1.6)
108
108
  io-console (0.5.11)
109
- io-wait (0.2.1)
110
109
  irb (1.4.1)
111
110
  reline (>= 0.3.0)
112
- loofah (2.13.0)
111
+ loofah (2.16.0)
113
112
  crass (~> 1.0.2)
114
113
  nokogiri (>= 1.5.9)
115
114
  mail (2.7.1)
@@ -118,7 +117,7 @@ GEM
118
117
  matrix (0.4.2)
119
118
  method_source (1.0.0)
120
119
  mini_mime (1.1.2)
121
- minitest (5.14.4)
120
+ minitest (5.15.0)
122
121
  minitest-spec-rails (6.1.0)
123
122
  minitest (>= 5.0)
124
123
  railties (>= 4.1)
@@ -130,78 +129,77 @@ GEM
130
129
  digest
131
130
  net-protocol
132
131
  timeout
133
- net-protocol (0.1.2)
134
- io-wait
132
+ net-protocol (0.1.3)
135
133
  timeout
136
134
  net-smtp (0.3.1)
137
135
  digest
138
136
  net-protocol
139
137
  timeout
140
138
  nio4r (2.5.8)
141
- nokogiri (1.13.1-arm64-darwin)
139
+ nokogiri (1.13.4-arm64-darwin)
142
140
  racc (~> 1.4)
143
- nokogiri (1.13.1-x86_64-darwin)
141
+ nokogiri (1.13.4-x86_64-darwin)
144
142
  racc (~> 1.4)
145
- nokogiri (1.13.1-x86_64-linux)
143
+ nokogiri (1.13.4-x86_64-linux)
146
144
  racc (~> 1.4)
147
- parallel (1.21.0)
148
- parser (3.1.0.0)
145
+ parallel (1.22.1)
146
+ parser (3.1.2.0)
149
147
  ast (~> 2.4.1)
150
- public_suffix (4.0.6)
148
+ public_suffix (4.0.7)
151
149
  racc (1.6.0)
152
150
  rack (2.2.3)
153
151
  rack-test (1.1.0)
154
152
  rack (>= 1.0, < 3)
155
- rails (7.0.1)
156
- actioncable (= 7.0.1)
157
- actionmailbox (= 7.0.1)
158
- actionmailer (= 7.0.1)
159
- actionpack (= 7.0.1)
160
- actiontext (= 7.0.1)
161
- actionview (= 7.0.1)
162
- activejob (= 7.0.1)
163
- activemodel (= 7.0.1)
164
- activerecord (= 7.0.1)
165
- activestorage (= 7.0.1)
166
- activesupport (= 7.0.1)
153
+ rails (7.0.2.4)
154
+ actioncable (= 7.0.2.4)
155
+ actionmailbox (= 7.0.2.4)
156
+ actionmailer (= 7.0.2.4)
157
+ actionpack (= 7.0.2.4)
158
+ actiontext (= 7.0.2.4)
159
+ actionview (= 7.0.2.4)
160
+ activejob (= 7.0.2.4)
161
+ activemodel (= 7.0.2.4)
162
+ activerecord (= 7.0.2.4)
163
+ activestorage (= 7.0.2.4)
164
+ activesupport (= 7.0.2.4)
167
165
  bundler (>= 1.15.0)
168
- railties (= 7.0.1)
166
+ railties (= 7.0.2.4)
169
167
  rails-dom-testing (2.0.3)
170
168
  activesupport (>= 4.2.0)
171
169
  nokogiri (>= 1.6)
172
170
  rails-html-sanitizer (1.4.2)
173
171
  loofah (~> 2.3)
174
- railties (7.0.1)
175
- actionpack (= 7.0.1)
176
- activesupport (= 7.0.1)
172
+ railties (7.0.2.4)
173
+ actionpack (= 7.0.2.4)
174
+ activesupport (= 7.0.2.4)
177
175
  method_source
178
176
  rake (>= 12.2)
179
177
  thor (~> 1.0)
180
178
  zeitwerk (~> 2.5)
181
179
  rainbow (3.1.1)
182
- rake (13.0.3)
180
+ rake (13.0.6)
183
181
  redcarpet (3.5.1)
184
- regexp_parser (2.2.0)
182
+ regexp_parser (2.3.1)
185
183
  reline (0.3.1)
186
184
  io-console (~> 0.5)
187
185
  rexml (3.2.5)
188
186
  rouge (3.28.0)
189
- rubocop (1.25.0)
187
+ rubocop (1.28.2)
190
188
  parallel (~> 1.10)
191
189
  parser (>= 3.1.0.0)
192
190
  rainbow (>= 2.2.2, < 4.0)
193
191
  regexp_parser (>= 1.8, < 3.0)
194
192
  rexml
195
- rubocop-ast (>= 1.15.1, < 2.0)
193
+ rubocop-ast (>= 1.17.0, < 2.0)
196
194
  ruby-progressbar (~> 1.7)
197
195
  unicode-display_width (>= 1.4.0, < 3.0)
198
- rubocop-ast (1.15.1)
199
- parser (>= 3.0.1.1)
200
- rubocop-performance (1.13.2)
196
+ rubocop-ast (1.17.0)
197
+ parser (>= 3.1.1.0)
198
+ rubocop-performance (1.13.3)
201
199
  rubocop (>= 1.7.0, < 2.0)
202
200
  rubocop-ast (>= 0.4.0)
203
201
  ruby-progressbar (1.11.0)
204
- sprockets (4.0.2)
202
+ sprockets (4.0.3)
205
203
  concurrent-ruby (~> 1.0)
206
204
  rack (> 1, < 3)
207
205
  sprockets-rails (3.4.2)
@@ -209,9 +207,9 @@ GEM
209
207
  activesupport (>= 5.2)
210
208
  sprockets (>= 3.0.0)
211
209
  sqlite3 (1.4.2)
212
- standard (1.7.0)
213
- rubocop (= 1.25.0)
214
- rubocop-performance (= 1.13.2)
210
+ standard (1.11.0)
211
+ rubocop (= 1.28.2)
212
+ rubocop-performance (= 1.13.3)
215
213
  strscan (3.0.1)
216
214
  thor (1.2.1)
217
215
  timeout (0.2.0)
@@ -1,4 +1,4 @@
1
- /* global fetch */
1
+ /* global fetch, Event */
2
2
  import { Controller } from '@hotwired/stimulus'
3
3
 
4
4
  export default class extends Controller {
@@ -18,11 +18,16 @@ export default class extends Controller {
18
18
  this.show()
19
19
  })
20
20
 
21
- // Typing
21
+ // Navigation
22
22
  this.inputTarget.addEventListener('keydown', (event) => {
23
23
  this.handleKeydown(event)
24
24
  })
25
25
 
26
+ // Typing
27
+ this.inputTarget.addEventListener('keyup', (event) => {
28
+ this.handleKeyup(event)
29
+ })
30
+
26
31
  // Clicked outside dropdown
27
32
  document.addEventListener('click', (event) => {
28
33
  this.handleOutsideClick(event)
@@ -30,20 +35,33 @@ export default class extends Controller {
30
35
  }
31
36
 
32
37
  handleKeydown (event) {
33
- this.show()
34
-
35
38
  const keyCode = parseInt(event.keyCode, 10)
36
39
  if (this.isArrowKey(keyCode)) {
37
40
  this.handleArrowKey(keyCode)
38
- } else if (this.isEnterKey(keyCode)) {
39
- this.selectActiveItem()
40
- } else {
41
- this.handleTextKey()
41
+ }
42
+
43
+ if (this.isEnterKey(keyCode)) {
44
+ this.selectActiveItem(event)
42
45
  }
43
46
  }
44
47
 
45
- selectActiveItem () {
46
- this.activeItem().click()
48
+ handleKeyup (event) {
49
+ // Ignore arrow keys or enters
50
+ const keyCode = parseInt(event.keyCode, 10)
51
+ if (this.isArrowKey(keyCode) || this.isEnterKey(keyCode)) {
52
+ return false
53
+ }
54
+
55
+ this.handleTextKey()
56
+ }
57
+
58
+ selectActiveItem (event) {
59
+ const activeItem = this.activeItem()
60
+ if (activeItem) {
61
+ this.deselectAll()
62
+ this.setValue(activeItem.getAttribute('value'))
63
+ event.preventDefault()
64
+ }
47
65
  }
48
66
 
49
67
  isEnterKey (keyCode) {
@@ -51,6 +69,7 @@ export default class extends Controller {
51
69
  }
52
70
 
53
71
  handleArrowKey (keyCode) {
72
+ this.show()
54
73
  switch (keyCode) {
55
74
  case 38:
56
75
  this.handleArrowUp()
@@ -72,8 +91,10 @@ export default class extends Controller {
72
91
 
73
92
  selectNextItem () {
74
93
  const next = this.nextItem()
75
- this.deselectAll()
76
- next.classList.add('active')
94
+ if (next) {
95
+ this.deselectAll()
96
+ next.classList.add('active')
97
+ }
77
98
  }
78
99
 
79
100
  nextItem () {
@@ -94,8 +115,10 @@ export default class extends Controller {
94
115
 
95
116
  selectPreviousItem () {
96
117
  const previous = this.previousItem()
97
- this.deselectAll()
98
- previous.classList.add('active')
118
+ if (previous) {
119
+ this.deselectAll()
120
+ previous.classList.add('active')
121
+ }
99
122
  }
100
123
 
101
124
  previousItem () {
@@ -160,13 +183,22 @@ export default class extends Controller {
160
183
  // 2. render html
161
184
  this.renderCollection(html)
162
185
  }).then(() => {
163
- // 3. sort by relevance
164
- // ...
165
- // 4. Highlight
186
+ // 3. Highlight
166
187
  this.highlight()
188
+ }).then(() => {
189
+ // 4. Preselect first result
190
+ this.activateFirstItem()
191
+ }).then(() => {
192
+ // 5. Show results
193
+ this.show()
167
194
  })
168
195
  }
169
196
 
197
+ activateFirstItem () {
198
+ this.deselectAll()
199
+ this.firstItem().classList.add('active')
200
+ }
201
+
170
202
  show () {
171
203
  if (this.isDropdownEmpty()) {
172
204
  this.dropdownTarget.classList.remove('d-none')
@@ -190,7 +222,7 @@ export default class extends Controller {
190
222
 
191
223
  fetchCollection () {
192
224
  if (this.isRemote()) {
193
- return fetch(this.urlValue).then((response) => {
225
+ return fetch(this.remoteURL()).then((response) => {
194
226
  return response.text()
195
227
  }).catch((error) => {
196
228
  console.error('The URL you provided for the autocomplete collection didn\'t return a successful result', error)
@@ -200,6 +232,29 @@ export default class extends Controller {
200
232
  }
201
233
  }
202
234
 
235
+ remoteURL () {
236
+ // Set dummy base in case a relative path is provided as remote URL
237
+ const base = 'https://example.com'
238
+ const url = new URL(this.urlValue, base)
239
+
240
+ // Update pagination params
241
+ const params = new URLSearchParams(url.search)
242
+ params.set('search', this.value())
243
+ params.set('page', '1')
244
+ params.set('per_page', '6')
245
+ url.search = params.toString()
246
+
247
+ // Convert to string
248
+ let urlString = url.toString()
249
+
250
+ // Convert back to relative url if needed
251
+ if (urlString.includes(base)) {
252
+ urlString = urlString.replace(base, '')
253
+ }
254
+
255
+ return urlString
256
+ }
257
+
203
258
  renderCollection (html) {
204
259
  this.dropdownTarget.innerHTML = html
205
260
  }
@@ -217,15 +272,23 @@ export default class extends Controller {
217
272
  text = text.replace(/<mark.*?>(.*?)<\/mark>/ig, '$1')
218
273
 
219
274
  // Highlight query
220
- const regex2 = new RegExp(`(${query})`, 'gi')
221
- text = text.replace(regex2, '<mark>$1</mark>')
275
+ if (query && query.length > 0) {
276
+ // Ignore all strings inside < >
277
+ const regex2 = new RegExp(`(?<!<[^>]*?)(&nbsp;)?(${query})`, 'gi')
278
+ text = text.replace(regex2, '<mark>$2</mark>')
279
+ }
222
280
 
223
281
  dropdownItem.innerHTML = text
224
282
  })
225
283
  }
226
284
 
227
285
  select (event) {
228
- this.inputTarget.value = event.target.getAttribute('value')
286
+ this.setValue(event.currentTarget.getAttribute('value'))
287
+ }
288
+
289
+ setValue (value) {
290
+ this.inputTarget.value = value
291
+ this.inputTarget.dispatchEvent(new Event('change'))
229
292
  this.hide()
230
293
  }
231
294
 
@@ -1,10 +1,6 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
 
3
3
  export default class extends Controller {
4
- static get targets () {
5
- return ['dateInput', 'startDateInput', 'endDateInput']
6
- }
7
-
8
4
  update (event) {
9
5
  const flatpickr = event.target._flatpickr
10
6
  const startDate = flatpickr.selectedDates[0]
@@ -15,11 +11,21 @@ export default class extends Controller {
15
11
  }
16
12
 
17
13
  setStartDateInputValue (value) {
18
- this.startDateInputTarget.value = value
14
+ const startDateInput = this.startDateInput()
15
+ startDateInput.value = value
19
16
  }
20
17
 
21
18
  setEndDateInputValue (value) {
22
- this.endDateInputTarget.value = value
19
+ const endDateInput = this.endDateInput()
20
+ endDateInput.value = value
21
+ }
22
+
23
+ startDateInput () {
24
+ return this.element.nextElementSibling
25
+ }
26
+
27
+ endDateInput () {
28
+ return this.startDateInput().nextElementSibling
23
29
  }
24
30
 
25
31
  formatDate (date) {
@@ -2,7 +2,13 @@ import { Controller } from '@hotwired/stimulus'
2
2
 
3
3
  export default class extends Controller {
4
4
  static get targets () {
5
- return ['button', 'popup']
5
+ return ['button', 'popup', 'conditional', 'operator', 'value', 'hidden', 'wrapper', 'template', 'row']
6
+ }
7
+
8
+ static get values () {
9
+ return {
10
+ name: String
11
+ }
6
12
  }
7
13
 
8
14
  // Attaches controller logic to the element itself
@@ -10,16 +16,7 @@ export default class extends Controller {
10
16
  connect () {
11
17
  this.element.controller = this
12
18
 
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
- }
19
+ this.updateHiddenValue()
23
20
  }
24
21
 
25
22
  toggle (event) {
@@ -41,6 +38,38 @@ export default class extends Controller {
41
38
  this.popupTarget.classList.add('closed')
42
39
  }
43
40
 
41
+ add (event) {
42
+ event.preventDefault()
43
+ const html = this.getTemplateHTML()
44
+ this.wrapperTarget.insertAdjacentHTML('beforeend', html)
45
+ }
46
+
47
+ remove (event) {
48
+ event.preventDefault()
49
+ const inputGroup = event.currentTarget.closest('[data-filter-target="row"]')
50
+ // Check if there is a previous element (the conditional select). If no element (first inputGroup), take the next element.
51
+ const conditional = inputGroup.previousElementSibling != null ? inputGroup.previousElementSibling : inputGroup.nextElementSibling
52
+
53
+ inputGroup.remove()
54
+ // If there is only one inputGroup, the conditional is null, so do not attempt to remove it
55
+ if (conditional != null) conditional.remove()
56
+
57
+ // After we remove the UI, run update to also remove filter from the hidden input
58
+ this.updateHiddenValue()
59
+
60
+ // Check if there are still inputs left, else remove filter
61
+ if (this.valueTargets.length === 0) {
62
+ this.removeFilter()
63
+ }
64
+ }
65
+
66
+ removeFilter () {
67
+ const form = this.buttonTarget.closest('form')
68
+ this.buttonTarget.remove()
69
+ this.popupTarget.remove()
70
+ form.submit()
71
+ }
72
+
44
73
  isClickedInside (event) {
45
74
  if (!event) {
46
75
  return false
@@ -50,4 +79,25 @@ export default class extends Controller {
50
79
  const inAddButton = event.target.dataset.action === 'click->filters#add'
51
80
  return (inPopup || inButton || inAddButton)
52
81
  }
82
+
83
+ updateHiddenValue () {
84
+ this.hiddenTarget.value = this.buildInstructionString()
85
+ }
86
+
87
+ buildInstructionString () {
88
+ let string = ''
89
+ for (const row of this.rowTargets) {
90
+ const conditional = row.previousElementSibling ? row.previousElementSibling.querySelector('[data-filter-target="conditional"]').value : null
91
+ const operator = row.querySelector('[data-filter-target="operator"]').value
92
+ const value = row.querySelector('[data-filter-target="value"]').value
93
+ string += `${conditional || ''}${operator}:${value}`
94
+ }
95
+
96
+ return string
97
+ }
98
+
99
+ getTemplateHTML () {
100
+ const template = this.templateTarget
101
+ return template.innerHTML
102
+ }
53
103
  }
@@ -0,0 +1,50 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static get targets () {
5
+ return ['original', 'operator', 'null']
6
+ }
7
+
8
+ connect () {
9
+ this.operatorTarget.addEventListener('change', () => this.handleOperatorChange())
10
+ this.handleOperatorChange()
11
+ }
12
+
13
+ handleOperatorChange () {
14
+ if (this.operatorTarget.value === 'is_null' || this.operatorTarget.value === 'is_not_null') {
15
+ this.toggleNullInput()
16
+ } else {
17
+ this.toggleOriginalInput()
18
+ }
19
+ }
20
+
21
+ toggleNullInput () {
22
+ this.hideOriginal()
23
+ this.showNull()
24
+ }
25
+
26
+ toggleOriginalInput () {
27
+ this.showOriginal()
28
+ this.hideNull()
29
+ }
30
+
31
+ hideOriginal () {
32
+ this.originalTarget.style.display = 'none'
33
+ this.originalTarget.setAttribute('data-filter-target', 'value_original')
34
+ }
35
+
36
+ showOriginal () {
37
+ this.originalTarget.style.display = 'block'
38
+ this.originalTarget.setAttribute('data-filter-target', 'value')
39
+ }
40
+
41
+ hideNull () {
42
+ this.nullTarget.style.display = 'none'
43
+ this.nullTarget.setAttribute('data-filter-target', 'value_null')
44
+ }
45
+
46
+ showNull () {
47
+ this.nullTarget.style.display = 'block'
48
+ this.nullTarget.setAttribute('data-filter-target', 'value')
49
+ }
50
+ }