active_element 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/Gemfile.lock +3 -7
  4. data/active_element.gemspec +1 -1
  5. data/app/assets/javascripts/active_element/application.js +3 -1
  6. data/app/assets/javascripts/active_element/popover.js +6 -0
  7. data/app/assets/javascripts/active_element/setup.js +12 -0
  8. data/app/assets/javascripts/active_element/{search_field.js → text_search_field.js} +2 -2
  9. data/app/assets/javascripts/active_element/toast.js +8 -0
  10. data/app/assets/stylesheets/active_element/application.scss +65 -1
  11. data/app/controllers/active_element/application_controller.rb +12 -18
  12. data/app/views/active_element/components/form/_label.html.erb +2 -2
  13. data/app/views/active_element/components/form/_templates.html.erb +8 -8
  14. data/app/views/active_element/components/form.html.erb +3 -3
  15. data/app/views/active_element/components/table/_collection_row.html.erb +3 -3
  16. data/app/views/active_element/components/table/collection.html.erb +1 -1
  17. data/app/views/active_element/components/table/item.html.erb +3 -3
  18. data/app/views/active_element/navbar/_menu.html.erb +2 -2
  19. data/app/views/layouts/active_element.html.erb +21 -23
  20. data/config/routes.rb +10 -1
  21. data/lib/active_element/components/button.rb +6 -41
  22. data/lib/active_element/components/form.rb +12 -3
  23. data/lib/active_element/components/text_search/active_record_authorization.rb +13 -0
  24. data/lib/active_element/components/text_search/authorization.rb +117 -0
  25. data/lib/active_element/components/text_search/component.rb +118 -0
  26. data/lib/active_element/components/text_search/sql.rb +107 -0
  27. data/lib/active_element/components/text_search.rb +23 -0
  28. data/lib/active_element/components/util/decorator.rb +2 -2
  29. data/lib/active_element/components/util/record_path.rb +84 -0
  30. data/lib/active_element/components/util.rb +7 -2
  31. data/lib/active_element/components.rb +1 -0
  32. data/lib/active_element/controller_action.rb +9 -10
  33. data/lib/active_element/controller_interface.rb +78 -0
  34. data/lib/active_element/permissions_check.rb +33 -29
  35. data/lib/active_element/permissions_report.rb +57 -0
  36. data/lib/active_element/route.rb +1 -1
  37. data/lib/active_element/routes.rb +1 -1
  38. data/lib/active_element/version.rb +1 -1
  39. data/lib/active_element.rb +24 -10
  40. data/lib/tasks/active_element.rake +1 -16
  41. data/rspec-documentation/pages/Components/Tables.md +2 -2
  42. data/rspec-documentation/spec_helper.rb +1 -1
  43. metadata +19 -12
  44. data/app/controllers/active_element/text_searches_controller.rb +0 -189
  45. data/lib/active_element/active_record_text_search_authorization.rb +0 -12
  46. data/lib/active_element/colorized_string.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 655d08db8eadabaadb33cb07a264edaa864eda4173c11bc3f2cf5655eb3a6227
4
- data.tar.gz: a194ffc1b2e3b0debe6f6bbc8173ed0ba8ae7125dfaf63b4e8794c01fc66f04a
3
+ metadata.gz: 55c4ed1b8eb3233fc4c071184ac145f01888d72b751fafafa68ff32873600e15
4
+ data.tar.gz: ce1fba3786c8b1084678a26f27bb72107fdfec3de8dec53e6ccf4d45e32d526c
5
5
  SHA512:
6
- metadata.gz: 2f0fc5677bfb2c341aedab2b4bf5f0c1150731bea95331f71bc0a1102a0bd8d9d89aba94c427e603328225feac7273ed5f6ca55616eb11f88852b8eeeae06a19
7
- data.tar.gz: 17e0e9b4dbc007e6ad6c7aabd941100a24188c461bfe6a35b781b7a4538b7b7b02b59c2733e5326f9946ac2097975be6205482a0f4fa04e35e34bf093c433baf
6
+ metadata.gz: 5f3461c71414a3264a4e8911e642a5ca2fddbcf8cb456eef49f777a86eae69b094a35e7e133401428d4168062198a88fe63e6c909e62dab46ca965d00d7aa85e
7
+ data.tar.gz: 469fb951cb7ccd0ab11ebb54b207d2c59ca5a8fbe2fc06f49d3ef58aaff69389870ec39e48b2c243a503ac036ae8d198e19c6120b7a4d881ba56a5b839f66e0f
data/.rubocop.yml CHANGED
@@ -2,6 +2,7 @@ require:
2
2
  - rubocop-rake
3
3
  - rubocop-rspec
4
4
  - rubocop-rails
5
+ - rubocop-factory_bot
5
6
 
6
7
  AllCops:
7
8
  NewCops: enable
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- active_element (0.0.3)
4
+ active_element (0.0.5)
5
5
  bootstrap (~> 5.3.0alpha3)
6
- faraday (~> 2.7)
7
6
  kaminari (~> 1.2)
7
+ paintbrush (~> 0.1.2)
8
8
  rails (~> 6.1)
9
9
  rouge (~> 4.1)
10
10
  sassc (~> 2.4)
@@ -102,10 +102,6 @@ GEM
102
102
  factory_bot_rails (5.2.0)
103
103
  factory_bot (~> 5.2.0)
104
104
  railties (>= 4.2.0)
105
- faraday (2.7.5)
106
- faraday-net_http (>= 2.0, < 3.1)
107
- ruby2_keywords (>= 0.0.4)
108
- faraday-net_http (3.0.2)
109
105
  ffi (1.15.5)
110
106
  globalid (1.1.0)
111
107
  activesupport (>= 5.0)
@@ -153,6 +149,7 @@ GEM
153
149
  racc (~> 1.4)
154
150
  orm_adapter (0.5.0)
155
151
  paint (2.3.0)
152
+ paintbrush (0.1.2)
156
153
  parallel (1.23.0)
157
154
  parser (3.2.2.1)
158
155
  ast (~> 2.4.1)
@@ -252,7 +249,6 @@ GEM
252
249
  rubocop-capybara (~> 2.17)
253
250
  rubocop-factory_bot (~> 2.22)
254
251
  ruby-progressbar (1.13.0)
255
- ruby2_keywords (0.0.5)
256
252
  sassc (2.4.0)
257
253
  ffi (~> 1.9)
258
254
  sassc-rails (2.1.2)
@@ -31,8 +31,8 @@ Gem::Specification.new do |spec|
31
31
  spec.metadata['rubygems_mfa_required'] = 'true'
32
32
 
33
33
  spec.add_runtime_dependency 'bootstrap', '~> 5.3.0alpha3'
34
- spec.add_runtime_dependency 'faraday', '~> 2.7'
35
34
  spec.add_runtime_dependency 'kaminari', '~> 1.2'
35
+ spec.add_runtime_dependency 'paintbrush', '~> 0.1.2'
36
36
  spec.add_runtime_dependency 'rails', '~> 6.1'
37
37
  spec.add_runtime_dependency 'rouge', '~> 4.1'
38
38
  spec.add_runtime_dependency 'sassc', '~> 2.4'
@@ -4,7 +4,9 @@
4
4
  //= require active_element/theme
5
5
  //= require active_element/secret
6
6
  //= require active_element/json_field
7
- //= require active_element/search_field
7
+ //= require active_element/text_search_field
8
8
  //= require active_element/form
9
9
  //= require active_element/confirm
10
10
  //= require active_element/pagination
11
+ //= require active_element/toast
12
+ //= require active_element/popover
@@ -0,0 +1,6 @@
1
+ (() => {
2
+ const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
3
+ const popoverList = popoverTriggerList.map(function (element) {
4
+ return new bootstrap.Popover(element)
5
+ })
6
+ })();
@@ -20,6 +20,17 @@
20
20
  return element;
21
21
  };
22
22
 
23
+ const navbar = document.querySelector('.navbar.application-menu');
24
+
25
+ window.addEventListener('scroll', () => {
26
+ if (window.scrollY > 50) {
27
+ navbar.classList.add('shrink');
28
+ } else {
29
+ navbar.classList.remove('shrink');
30
+ }
31
+ });
32
+
33
+
23
34
  const ActiveElement = {
24
35
  log: (message) => { console.log(`[ActiveElement] ${message}`); },
25
36
  _id: 0,
@@ -28,6 +39,7 @@
28
39
  cloneElement,
29
40
  components: {},
30
41
  jsonData: {},
42
+ controller_path: document.querySelector('meta[name="active_element_controller_path"]').content
31
43
  };
32
44
 
33
45
  window.ActiveElement = ActiveElement;
@@ -37,7 +37,7 @@
37
37
  resultsItem.innerText = attributes.length === 0 ? value : `${attributes.join(', ')} (${value})`;
38
38
  resultsItem.addEventListener('click', () => {
39
39
  hiddenInput.value = value;
40
- element.value = attributes.length == 0 ? value : `${attributes.join(', ')} (${value})`;
40
+ element.value = value;
41
41
  searchResultsContainer.replaceChildren();
42
42
  searchResultsContainer.classList.add('d-none');
43
43
  });
@@ -97,7 +97,7 @@
97
97
  searchResultsContainer.replaceChildren();
98
98
 
99
99
  fetch(
100
- '/_text_search/',
100
+ `/${ActiveElement.controller_path}/_active_element_text_search/`,
101
101
  {
102
102
  method: 'POST',
103
103
  headers: { 'Content-Type': 'application/json' },
@@ -0,0 +1,8 @@
1
+ (() => {
2
+ const elements = [].slice.call(document.querySelectorAll('.toast'));
3
+ const toasts = elements.map(function (element) {
4
+ return new bootstrap.Toast(element, { animation: true, autohide: true, delay: 10000 })
5
+ });
6
+
7
+ toasts.forEach((toast) => toast.show());
8
+ })();
@@ -1,7 +1,22 @@
1
1
  @import "variables";
2
2
  @import "bootstrap";
3
3
 
4
- .navbar-brand {
4
+ .navbar.application-menu {
5
+ height: 5rem;
6
+ transition: height 0.5s ease-in-out, background-position 0.8s ease-in-out;
7
+ background-position: right 1.2rem center;
8
+ background-repeat: no-repeat;
9
+
10
+ &.shrink {
11
+ height: 3rem;
12
+ background-position: right -50rem center;
13
+ }
14
+
15
+ &:hover {
16
+ height: 5rem;
17
+ background-position: right 1.2rem center;
18
+ background-repeat: no-repeat;
19
+ }
5
20
  }
6
21
 
7
22
  td.action-column {
@@ -51,6 +66,55 @@ form {
51
66
  }
52
67
  }
53
68
 
69
+ .search-field-results {
70
+ min-width: 20rem;
71
+ position: absolute;
72
+ .search-field-result {
73
+ cursor: pointer;
74
+ &:hover {
75
+ background-color: #{$blue};
76
+ color: #{$white};
77
+ }
78
+ }
79
+ }
80
+
81
+ .flash.toast {
82
+ position: absolute;
83
+ margin-top: 2rem;
84
+ margin-right: 1rem;
85
+ padding: 0;
86
+
87
+ &.notice {
88
+ .toast-body {
89
+ background: #{$blue-100};
90
+ }
91
+ }
92
+
93
+ &.alert {
94
+ .toast-body {
95
+ background: #{$red-100};
96
+ }
97
+ }
98
+ }
99
+
100
+ [data-bs-theme="dark"] {
101
+ .flash.toast {
102
+ &.notice {
103
+ .toast-body {
104
+ color: #{$white};
105
+ background: #{$blue-700};
106
+ }
107
+ }
108
+
109
+ &.alert {
110
+ .toast-body {
111
+ color: #{$white};
112
+ background: #{$red-700};
113
+ }
114
+ }
115
+ }
116
+ }
117
+
54
118
  #theme-select {
55
119
  font-size: 1.5rem;
56
120
  padding: 0.2rem 0.8rem;
@@ -8,34 +8,28 @@ module ActiveElement
8
8
 
9
9
  layout 'active_element'
10
10
 
11
- before_action -> { authenticate_user! }
12
- before_action -> { ActiveElement::ControllerAction.new(self).process_action }
13
-
14
- helper_method :active_element_component
15
- helper_method :render_active_element_hook
16
-
17
- def self.permit_user(permissions, **kwargs)
18
- active_element_permissions << [permissions, kwargs]
19
-
20
- nil
11
+ def self.active_element
12
+ @active_element ||= ActiveElement::ControllerInterface.new(self)
21
13
  end
22
14
 
23
- def active_element_component
24
- @active_element_component ||= ActiveElement::Component.new(self)
15
+ def active_element
16
+ @active_element ||= ActiveElement::ControllerInterface.new(self.class, self)
25
17
  end
26
18
 
19
+ before_action -> { active_element.authenticator&.call }
20
+ before_action -> { ActiveElement::ControllerAction.new(self).process_action }
21
+
22
+ helper_method :active_element
23
+ helper_method :render_active_element_hook
24
+
27
25
  def render_active_element_hook(hook)
28
26
  render_to_string partial: hook
29
27
  rescue ActionView::MissingTemplate
30
28
  nil
31
29
  end
32
30
 
33
- def missing_template_store
34
- @missing_template_store ||= {}
35
- end
36
-
37
- def self.active_element_permissions
38
- @active_element_permissions ||= []
31
+ def _active_element_text_search
32
+ render(**ActiveElement::Components::TextSearch::Component.new(controller: self).response)
39
33
  end
40
34
  end
41
35
  end
@@ -1,6 +1,6 @@
1
- <%= form.label field, options[:label] do %>
1
+ <%= form.label field do %>
2
+ <%= options[:label] %>
2
3
  <% if options[:description].present? %>
3
- <%= options[:label] %>
4
4
  <button type="button"
5
5
  style="background: none; border: none; outline: 0; position: absolute; margin-top: 0.3rem"
6
6
  data-bs-toggle="popover"
@@ -17,25 +17,25 @@
17
17
 
18
18
  <select id="json-select-template" class="form-select m-1 json-select-field"></select>
19
19
 
20
- <%= active_element_component.destroy_button id: 'json-delete-button-template',
20
+ <%= active_element.component.destroy_button id: 'json-delete-button-template',
21
21
  class: 'button-sm json-delete-button' %>
22
22
 
23
- <%= active_element_component.button 'Delete Item', type: 'danger', id: 'json-delete-object-button-template',
23
+ <%= active_element.component.button 'Delete Item', type: 'danger', id: 'json-delete-object-button-template',
24
24
  class: 'json-delete-button w-25 float-end json-delete-object-button' %>
25
25
 
26
- <%= active_element_component.button id: 'json-append-button-template',
26
+ <%= active_element.component.button id: 'json-append-button-template',
27
27
  class: 'btn-sm mt-3 mb-3 json-add-field-button' %>
28
28
 
29
- <%= active_element_component.button 'Hide', id: 'json-expand-collapse-button-template',
29
+ <%= active_element.component.button 'Hide', id: 'json-expand-collapse-button-template',
30
30
  class: 'float-end expand-collapse-button' %>
31
31
 
32
32
  </div>
33
33
 
34
- <%= active_element_component.button 'Expand Form', id: 'form-expand-button-template', class: 'mb-1 btn-sm' do %>
34
+ <%= active_element.component.button 'Expand Form', id: 'form-expand-button-template', class: 'mb-1 btn-sm' do %>
35
35
  <i class="fa-solid fa-fw fa-up-right-and-down-left-from-center"></i>
36
36
  <% end %>
37
37
 
38
- <%= active_element_component.button 'Collapse Form', id: 'form-collapse-button-template', class: 'mb-1 btn-sm' do %>
38
+ <%= active_element.component.button 'Collapse Form', id: 'form-collapse-button-template', class: 'mb-1 btn-sm' do %>
39
39
  <i class="fa-solid fa-fw fa-down-left-and-up-right-to-center"></i>
40
40
  <% end %>
41
41
 
@@ -76,10 +76,10 @@
76
76
  </div>
77
77
 
78
78
  <div id="form-confirm-button-template">
79
- <%= active_element_component.button 'Confirm' %>
79
+ <%= active_element.component.button 'Confirm' %>
80
80
  </div>
81
81
 
82
82
  <div id="form-cancel-button-template">
83
- <%= active_element_component.button 'Cancel' %>
83
+ <%= active_element.component.button 'Cancel' %>
84
84
  </div>
85
85
  </div>
@@ -1,5 +1,5 @@
1
1
  <% if destroy %>
2
- <%= active_element_component.destroy_button(record, float: 'end') %>
2
+ <%= active_element.component.destroy_button(record, float: 'end') %>
3
3
  <% end %>
4
4
 
5
5
  <% if modal %>
@@ -7,7 +7,7 @@
7
7
  <div class="mt-4 mb-5 ms-3">
8
8
 
9
9
  <div class="p-3 pb-4 border-bottom d-inline">
10
- <%= active_element_component.button(
10
+ <%= active_element.component.button(
11
11
  title: title.presence || 'Show Form',
12
12
  icon: 'arrow-up-right-dots',
13
13
  data: { field_type: 'form-modal', form_id: id, form_title: title }) %>
@@ -40,7 +40,7 @@
40
40
  <%= form_with local: true, **kwargs, method: method, id: id, class: "#{class_name} m-3 #{modal == false && expanded == false ? 'd-none' : nil}" do |form| %>
41
41
  <% if %i[top both].include?(submit_position) %>
42
42
  <div class="form-group sticky-top" style="top: 0.5rem;">
43
- <%= form.submit submit_label, class: 'btn btn-success float-end' %>
43
+ <%= form.submit submit_label, class: "btn btn-#{method == :post ? 'success' : 'primary'} float-end" %>
44
44
  </div>
45
45
  <% end %>
46
46
 
@@ -12,19 +12,19 @@
12
12
 
13
13
  <% if show %>
14
14
  <td class="<%= "#{class_name}-show" %> action-column text-end">
15
- <%= active_element_component.show_button(item, show, class: 'btn-sm') %>
15
+ <%= active_element.component.show_button(item, show, class: 'btn-sm') %>
16
16
  </td>
17
17
  <% end %>
18
18
 
19
19
  <% if edit %>
20
20
  <td class="<%= "#{class_name}-edit" %> action-column text-end">
21
- <%= active_element_component.edit_button(item, show, class: 'btn-sm') %>
21
+ <%= active_element.component.edit_button(item, show, class: 'btn-sm') %>
22
22
  </td>
23
23
  <% end %>
24
24
 
25
25
  <% if destroy %>
26
26
  <td class="<%= "#{class_name}-destroy" %> action-column text-end">
27
- <%= active_element_component.destroy_button(item, destroy, class: 'btn-sm') %>
27
+ <%= active_element.component.destroy_button(item, destroy, class: 'btn-sm') %>
28
28
  </td>
29
29
  <% end %>
30
30
  </tr>
@@ -1,5 +1,5 @@
1
1
  <% if new %>
2
- <%= active_element_component.new_button(collection, float: 'end', class: 'mb-3') %>
2
+ <%= active_element.component.new_button(collection, float: 'end', class: 'mb-3') %>
3
3
  <% end %>
4
4
 
5
5
  <% if display_pagination %>
@@ -1,13 +1,13 @@
1
1
  <% if destroy && item.present? %>
2
- <%= active_element_component.destroy_button(item, destroy, float: 'end') %>
2
+ <%= active_element.component.destroy_button(item, destroy, float: 'end') %>
3
3
  <% end %>
4
4
 
5
5
  <% if edit && item.present? %>
6
- <%= active_element_component.edit_button(item, edit, float: 'end') %>
6
+ <%= active_element.component.edit_button(item, edit, float: 'end') %>
7
7
  <% end %>
8
8
 
9
9
  <% if new %>
10
- <%= active_element_component.new_button(item, float: 'end', class: 'mb-3') %>
10
+ <%= active_element.component.new_button(item, float: 'end', class: 'mb-3') %>
11
11
  <% end %>
12
12
 
13
13
  <table class="<%= class_name %> table" style="<%= style %>">
@@ -7,14 +7,14 @@
7
7
  </button>
8
8
  <div class="collapse navbar-collapse" id="navbarSupportedContent">
9
9
  <ul class="navbar-nav me-auto mb-2 mb-lg-0">
10
- <% ActiveElement.navbar_items(current_user).each do |navbar_item| %>
10
+ <% ActiveElement.navbar_items(active_element.current_user).each do |navbar_item| %>
11
11
  <li class="nav-item">
12
12
  <%=
13
13
  link_to navbar_item.fetch(:title) { navbar_item.fetch(:label) },
14
14
  navbar_item.fetch(:path),
15
15
  class: "nav-link #{
16
16
  ActiveElement.active_path_class(
17
- user: current_user,
17
+ user: active_element.current_user,
18
18
  current_navbar_item: navbar_item,
19
19
  current_path: request.path,
20
20
  controller_path: controller_path,
@@ -7,18 +7,15 @@
7
7
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.9.2/umd/popper.min.js" integrity="sha512-2rNj2KJ+D8s1ceNasTIex6z4HWyOnEYLVC3FigGOmyQCZc2eBXKgOxQmo3oKLHyfcj53uz4QMsRCWNbLd32Q1g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
8
8
 
9
9
  <%= stylesheet_link_tag 'active_element/application', 'data-turbolinks-track': 'reload' %>
10
- <%= javascript_include_tag 'active_element/application', 'data-turbolinks-track': 'reload' %>
11
10
 
12
11
  <% if Rails.application.assets.find_asset('application.css').present? %>
13
12
  <%= stylesheet_link_tag 'application', 'data-turbolinks-track': 'reload' %>
14
13
  <% end %>
15
14
 
16
- <% if Rails.application.assets.find_asset('application.js').present? %>
17
- <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
18
- <% end %>
19
-
20
15
  <%= csrf_meta_tag %>
21
16
 
17
+ <meta name="active_element_controller_path" content="<%= controller_path %>">
18
+
22
19
  <%= render_active_element_hook 'active_element/after_head' %>
23
20
  </head>
24
21
 
@@ -28,11 +25,21 @@
28
25
  <%= render_active_element_hook 'active_element/after_navbar' %>
29
26
 
30
27
  <% flash.each do |type, message| %>
31
- <div class="flash card text-bg-<%= { notice: 'info', alert: 'danger' }.fetch(type.to_sym, 'info') %> flash <%= type %> float-end">
32
- <div class="card-body">
33
- <%= message %>
34
- <button type="button" class="btn-close" aria-label="Close"></button>
35
- </div>
28
+ <div class="flash toast <%= type %> position-absolute top-0 end-0 mt-5" role="alert" aria-live="assertive" aria-atomic="true">
29
+ <div class="toast-header">
30
+ <% if type == 'notice' %>
31
+ <i class="fa-solid me-2 text-info fa-circle-info"></i>
32
+ <% elsif type == 'alert' %>
33
+ <i class="fa-solid me-2 text-danger fa-circle-exclamation"></i>
34
+ <% else %>
35
+ <i class="fa-solid me-2 text-info fa-circle-info"></i>
36
+ <% end %>
37
+ <strong class="me-auto"><%= type.titleize %></strong>
38
+ <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
39
+ </div>
40
+ <div class="toast-body fw-bold align-middle">
41
+ <%= message %>
42
+ </div>
36
43
  </div>
37
44
  <% end %>
38
45
 
@@ -48,18 +55,9 @@
48
55
 
49
56
  <%= render_active_element_hook 'active_element/after_content' %>
50
57
 
51
- <script>
52
- var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
53
- var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
54
- return new bootstrap.Popover(popoverTriggerEl)
55
- })
56
-
57
- document.querySelectorAll('.flash .btn-close').forEach(element => {
58
- element.onclick = ev => {
59
- ev.stopPropagation();
60
- ev.target.parentElement.parentElement.classList.add('d-none');
61
- };
62
- });
63
- </script>
58
+ <%= javascript_include_tag 'active_element/application', 'data-turbolinks-track': 'reload' %>
59
+ <% if Rails.application.assets.find_asset('application.js').present? %>
60
+ <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
61
+ <% end %>
64
62
  </body>
65
63
  </html>
data/config/routes.rb CHANGED
@@ -1,5 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  ActiveElement::Engine.routes.draw do
4
- post '/_text_search', controller: 'active_element/text_searches', action: 'create'
4
+ ActiveElement.eager_load_controllers
5
+
6
+ ActiveElement::ApplicationController.descendants.map do |descendant|
7
+ post "#{descendant.controller_path}/_active_element_text_search",
8
+ controller: descendant.controller_path,
9
+ action: '_active_element_text_search'
10
+
11
+ # Permissions for text search are managed by ActiveElement::Components::TextSearch::Authorization
12
+ descendant.active_element.permit_action :_active_element_text_search, always: true
13
+ end
5
14
  end
@@ -3,7 +3,6 @@
3
3
  module ActiveElement
4
4
  module Components
5
5
  # A clickable button.
6
- # rubocop:disable Metrics/ClassLength
7
6
  class Button
8
7
  # rubocop:disable Metrics/MethodLength
9
8
  def initialize(controller, record, flag_or_options, confirm: false, type: :primary, method: nil,
@@ -75,45 +74,6 @@ module ActiveElement
75
74
  { 'end' => 'float-end', 'start' => 'float-start', nil => nil }.fetch(float)
76
75
  end
77
76
 
78
- def namespace_prefix
79
- # XXX: We guess the namespace from the current controller's module name. This will work
80
- # most of the time but will break the current record's controller exists in a different
81
- # namespace to the current controller, e.g. `BackEndAdmin::UsersController` and
82
- # `FrontEndAdmin::ThemesController` - if `FrontEndAdmin::ThemesController` renders a
83
- # collection of `User` objects, the "show" path will be wrong:
84
- # `front_end_admin_user_path`. Maybe descend through the full controller class tree to
85
- # find a best match ?
86
- namespace = controller.class.name.deconstantize.underscore
87
- return nil if namespace.blank?
88
-
89
- "#{namespace}_"
90
- end
91
-
92
- def record_path
93
- return nil if record.nil?
94
-
95
- controller.helpers.public_send(default_record_path, record)
96
- rescue NoMethodError
97
- controller.helpers.public_send(sti_record_path, record)
98
- end
99
-
100
- def default_record_path
101
- "#{record_path_prefix}#{namespace_prefix}#{record_name}_path"
102
- end
103
-
104
- def sti_record_path
105
- "#{record_path_prefix}#{namespace_prefix}#{sti_record_name}_path"
106
- end
107
-
108
- def record_path_prefix
109
- case type
110
- when :edit
111
- 'edit_'
112
- when :new
113
- 'new_'
114
- end
115
- end
116
-
117
77
  def title
118
78
  return flag_or_options[:title] if flag_or_options.is_a?(Hash) && flag_or_options[:title].present?
119
79
  return default_action_title if %i[show destroy edit new].include?(type)
@@ -150,7 +110,12 @@ module ActiveElement
150
110
  def sti_record_name
151
111
  Util.sti_record_name(record)
152
112
  end
113
+
114
+ def record_path
115
+ return nil unless record.class.is_a?(ActiveModel::Naming)
116
+
117
+ Util::RecordPath.new(record: record, controller: controller, type: type).path
118
+ end
153
119
  end
154
120
  end
155
- # rubocop:enable Metrics/ClassLength
156
121
  end
@@ -22,6 +22,7 @@ module ActiveElement
22
22
  @modal = modal
23
23
  @kwargs = kwargs
24
24
  @columns = columns
25
+ @action = kwargs.delete(:action) { default_action }
25
26
  @method = kwargs.delete(:method) { default_method }.to_s.downcase.to_sym
26
27
  end
27
28
  # rubocop:enable Metrics/MethodLength
@@ -30,7 +31,7 @@ module ActiveElement
30
31
  'active_element/components/form'
31
32
  end
32
33
 
33
- def locals # rubocop:disable Metrics/MethodLength
34
+ def locals # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
34
35
  {
35
36
  component: self,
36
37
  fields: Util::FormFieldMapping.new(record, fields, i18n).fields_with_types_and_options,
@@ -39,6 +40,7 @@ module ActiveElement
39
40
  submit_position: submit_position,
40
41
  class_name: class_name,
41
42
  method: method,
43
+ action: action,
42
44
  kwargs: kwargs,
43
45
  destroy: destroy,
44
46
  modal: modal,
@@ -130,7 +132,8 @@ module ActiveElement
130
132
 
131
133
  private
132
134
 
133
- attr_reader :fields, :submit, :title, :kwargs, :item, :method, :destroy, :modal, :expanded, :columns
135
+ attr_reader :fields, :submit, :title, :kwargs, :item, :method, :action,
136
+ :destroy, :modal, :expanded, :columns
134
137
 
135
138
  def valid_field?(field)
136
139
  return true if record.respond_to?("#{field}_changed?") && !record.public_send("#{field}_changed?")
@@ -197,7 +200,7 @@ module ActiveElement
197
200
 
198
201
  def default_method
199
202
  case controller.action_name
200
- when 'edit'
203
+ when 'edit', 'update'
201
204
  'PATCH'
202
205
  when 'index'
203
206
  'GET'
@@ -205,6 +208,12 @@ module ActiveElement
205
208
  'POST'
206
209
  end
207
210
  end
211
+
212
+ def default_action
213
+ return controller.request.path unless record.is_a?(ActiveModel::Naming)
214
+
215
+ Util::RecordPath.new(record: record, controller: controller).path
216
+ end
208
217
  end
209
218
  end
210
219
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ ActiveRecord::Base.class_eval do
4
+ class << self
5
+ def authorize_active_element_text_search(with:, providing:)
6
+ ActiveElement::Components::TextSearch.register_authorized_text_search(
7
+ model: self,
8
+ with: with,
9
+ providing: providing
10
+ )
11
+ end
12
+ end
13
+ end