activeadmin 4.0.0.beta2 → 4.0.0.beta22

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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -2
  3. data/CONTRIBUTING.md +13 -13
  4. data/README.md +4 -4
  5. data/UPGRADING.md +274 -0
  6. data/app/controllers/active_admin/base_controller/authorization.rb +1 -1
  7. data/app/controllers/active_admin/base_controller/menu.rb +2 -2
  8. data/app/controllers/active_admin/resource_controller/data_access.rb +9 -4
  9. data/app/controllers/active_admin/resource_controller/decorators.rb +1 -1
  10. data/app/controllers/active_admin/resource_controller/streaming.rb +3 -3
  11. data/app/controllers/active_admin/resource_controller.rb +1 -1
  12. data/app/helpers/active_admin/display_helper.rb +3 -3
  13. data/app/helpers/active_admin/form_helper.rb +1 -1
  14. data/app/helpers/active_admin/layout_helper.rb +2 -2
  15. data/app/javascript/active_admin/features/batch_actions.js +10 -4
  16. data/app/javascript/active_admin/features/main_menu.js +3 -2
  17. data/app/views/active_admin/_flash_messages.html.erb +4 -4
  18. data/app/views/active_admin/_main_navigation.html.erb +28 -11
  19. data/app/views/active_admin/_page_header.html.erb +2 -2
  20. data/app/views/active_admin/_site_footer.html.erb +1 -1
  21. data/app/views/active_admin/_site_header.html.erb +7 -7
  22. data/app/views/active_admin/devise/confirmations/new.html.erb +1 -1
  23. data/app/views/active_admin/devise/passwords/edit.html.erb +1 -1
  24. data/app/views/active_admin/devise/passwords/new.html.erb +1 -1
  25. data/app/views/active_admin/devise/registrations/new.html.erb +1 -1
  26. data/app/views/active_admin/devise/sessions/new.html.erb +1 -1
  27. data/app/views/active_admin/devise/unlocks/new.html.erb +1 -1
  28. data/app/views/active_admin/kaminari/_gap.html.erb +1 -1
  29. data/app/views/active_admin/kaminari/_next_page.html.erb +1 -1
  30. data/app/views/active_admin/kaminari/_prev_page.html.erb +1 -1
  31. data/app/views/active_admin/page/index.html.arb +1 -2
  32. data/app/views/active_admin/resource/_form.html.arb +0 -1
  33. data/app/views/active_admin/resource/_form_default.html.arb +0 -1
  34. data/app/views/active_admin/resource/_index_as_table_default.html.arb +0 -1
  35. data/app/views/active_admin/resource/_show_default.html.arb +0 -1
  36. data/app/views/active_admin/resource/edit.html.arb +0 -1
  37. data/app/views/active_admin/resource/index.html.arb +1 -2
  38. data/app/views/active_admin/resource/new.html.arb +0 -1
  39. data/app/views/active_admin/resource/show.html.arb +0 -1
  40. data/app/views/active_admin/shared/_resource_comments.html.erb +7 -7
  41. data/app/views/active_admin/shared/_sidebar_section.html.arb +0 -1
  42. data/app/views/layouts/active_admin.html.erb +1 -1
  43. data/config/importmap.rb +5 -0
  44. data/config/locales/ar.yml +136 -100
  45. data/config/locales/az.yml +103 -102
  46. data/config/locales/bg.yml +91 -90
  47. data/config/locales/bs.yml +95 -94
  48. data/config/locales/ca.yml +131 -77
  49. data/config/locales/cs.yml +82 -81
  50. data/config/locales/da.yml +102 -101
  51. data/config/locales/de.yml +130 -125
  52. data/config/locales/el.yml +94 -93
  53. data/config/locales/en-CA.yml +104 -103
  54. data/config/locales/en-GB.yml +104 -103
  55. data/config/locales/en.yml +134 -130
  56. data/config/locales/eo.yml +107 -106
  57. data/config/locales/es-MX.yml +70 -69
  58. data/config/locales/es.yml +129 -122
  59. data/config/locales/fa.yml +91 -90
  60. data/config/locales/fi.yml +84 -83
  61. data/config/locales/fr.yml +135 -126
  62. data/config/locales/he.yml +103 -102
  63. data/config/locales/hr.yml +95 -94
  64. data/config/locales/hu.yml +76 -75
  65. data/config/locales/id.yml +97 -96
  66. data/config/locales/it.yml +134 -124
  67. data/config/locales/ja.yml +109 -104
  68. data/config/locales/ko.yml +136 -92
  69. data/config/locales/lt.yml +106 -105
  70. data/config/locales/lv.yml +70 -69
  71. data/config/locales/mk.yml +100 -99
  72. data/config/locales/nb.yml +98 -97
  73. data/config/locales/nl.yml +113 -114
  74. data/config/locales/pl.yml +137 -130
  75. data/config/locales/pt-BR.yml +134 -105
  76. data/config/locales/pt-PT.yml +71 -70
  77. data/config/locales/ro.yml +74 -73
  78. data/config/locales/ru.yml +142 -106
  79. data/config/locales/sk.yml +131 -130
  80. data/config/locales/sv-SE.yml +126 -125
  81. data/config/locales/tr.yml +105 -104
  82. data/config/locales/uk.yml +140 -103
  83. data/config/locales/vi.yml +125 -124
  84. data/config/locales/zh-CN.yml +135 -125
  85. data/config/locales/zh-TW.yml +136 -100
  86. data/lib/active_admin/application.rb +5 -5
  87. data/lib/active_admin/application_settings.rb +1 -1
  88. data/lib/active_admin/async_count.rb +21 -0
  89. data/lib/active_admin/batch_actions/resource_extension.rb +2 -2
  90. data/lib/active_admin/batch_actions/views/batch_action_form.rb +1 -1
  91. data/lib/active_admin/batch_actions/views/selection_cells.rb +1 -1
  92. data/lib/active_admin/batch_actions.rb +4 -4
  93. data/lib/active_admin/collection_decorator.rb +1 -1
  94. data/lib/active_admin/csv_builder.rb +1 -1
  95. data/lib/active_admin/dependency.rb +1 -1
  96. data/lib/active_admin/dsl.rb +2 -2
  97. data/lib/active_admin/dynamic_settings_node.rb +2 -2
  98. data/lib/active_admin/engine.rb +5 -2
  99. data/lib/active_admin/filters/active.rb +2 -2
  100. data/lib/active_admin/filters/active_filter.rb +1 -7
  101. data/lib/active_admin/filters/forms.rb +0 -3
  102. data/lib/active_admin/filters/resource_extension.rb +2 -2
  103. data/lib/active_admin/filters.rb +5 -5
  104. data/lib/active_admin/form_builder.rb +4 -9
  105. data/lib/active_admin/helpers/optional_display.rb +1 -1
  106. data/lib/active_admin/inputs/filters/base/search_method_select.rb +2 -2
  107. data/lib/active_admin/inputs/filters/select_input.rb +1 -3
  108. data/lib/active_admin/localizers.rb +1 -1
  109. data/lib/active_admin/menu.rb +2 -2
  110. data/lib/active_admin/menu_item.rb +1 -1
  111. data/lib/active_admin/namespace.rb +1 -1
  112. data/lib/active_admin/namespace_settings.rb +1 -1
  113. data/lib/active_admin/orm/active_record/comments/views/active_admin_comments.rb +1 -1
  114. data/lib/active_admin/orm/active_record/comments/views.rb +2 -2
  115. data/lib/active_admin/orm/active_record/comments.rb +7 -7
  116. data/lib/active_admin/orm/active_record.rb +1 -1
  117. data/lib/active_admin/pundit_adapter.rb +4 -4
  118. data/lib/active_admin/resource/action_items.rb +3 -3
  119. data/lib/active_admin/resource/attributes.rb +8 -1
  120. data/lib/active_admin/resource/belongs_to.rb +0 -1
  121. data/lib/active_admin/resource/naming.rb +1 -1
  122. data/lib/active_admin/resource/page_presenters.rb +2 -2
  123. data/lib/active_admin/resource/sidebars.rb +1 -1
  124. data/lib/active_admin/resource.rb +21 -19
  125. data/lib/active_admin/resource_collection.rb +1 -1
  126. data/lib/active_admin/resource_dsl.rb +2 -2
  127. data/lib/active_admin/router.rb +5 -5
  128. data/lib/active_admin/scope.rb +10 -0
  129. data/lib/active_admin/version.rb +1 -1
  130. data/lib/active_admin/view_helpers/method_or_proc_helper.rb +1 -1
  131. data/lib/active_admin/views/components/active_admin_form.rb +2 -2
  132. data/lib/active_admin/views/components/attributes_table.rb +10 -4
  133. data/lib/active_admin/views/components/paginated_collection.rb +4 -1
  134. data/lib/active_admin/views/components/panel.rb +1 -1
  135. data/lib/active_admin/views/components/scopes.rb +26 -4
  136. data/lib/active_admin/views/components/table_for.rb +7 -2
  137. data/lib/active_admin/views/index_as_table.rb +22 -6
  138. data/lib/active_admin.rb +11 -6
  139. data/lib/activeadmin.rb +1 -1
  140. data/lib/generators/active_admin/assets/templates/active_admin.css +3 -3
  141. data/lib/generators/active_admin/assets/templates/tailwind.config.js +9 -5
  142. data/lib/generators/active_admin/devise/devise_generator.rb +2 -2
  143. data/lib/generators/active_admin/install/templates/active_admin.rb.erb +1 -1
  144. data/lib/generators/active_admin/resource/resource_generator.rb +6 -2
  145. data/lib/generators/active_admin/resource/templates/resource.rb.erb +2 -2
  146. data/plugin.js +424 -0
  147. data/vendor/javascript/flowbite.js +1 -2
  148. data/vendor/javascript/rails_ujs_esm.js +1 -0
  149. metadata +35 -20
  150. data/config/locales/de-CH.yml +0 -84
  151. data/lib/active_admin/views/components/tabs.rb +0 -40
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
- require "active_admin/views"
3
- require "active_admin/orm/active_record/comments/views/active_admin_comments"
2
+ require_relative "../../../views"
3
+ require_relative "views/active_admin_comments"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
- require "active_admin/orm/active_record/comments/views"
3
- require "active_admin/orm/active_record/comments/namespace_helper"
4
- require "active_admin/orm/active_record/comments/resource_helper"
2
+ require_relative "comments/views"
3
+ require_relative "comments/namespace_helper"
4
+ require_relative "comments/resource_helper"
5
5
 
6
6
  # Add the comments configuration
7
7
  ActiveAdmin::Application.inheritable_setting :comments, true
@@ -93,13 +93,13 @@ ActiveAdmin.after_load do |app|
93
93
 
94
94
  index do
95
95
  column I18n.t("active_admin.comments.resource_type"), :resource_type
96
- column I18n.t("active_admin.comments.resource"), :resource, class: "min-w-[7rem]"
96
+ column I18n.t("active_admin.comments.resource"), :resource, class: "min-w-28"
97
97
  column I18n.t("active_admin.comments.author_type"), :author_type
98
98
  column I18n.t("active_admin.comments.author"), :author
99
- column I18n.t("active_admin.comments.body"), :body, class: "min-w-[16rem]" do |comment|
100
- truncate(comment.body, length: 60, separator: " ")
99
+ column I18n.t("active_admin.comments.body"), :body, class: "min-w-52" do |comment|
100
+ truncate(comment.body, length: 50, separator: " ")
101
101
  end
102
- column I18n.t("active_admin.comments.created_at"), :created_at, class: "min-w-[13rem]"
102
+ column I18n.t("active_admin.comments.created_at"), :created_at, class: "min-w-52"
103
103
  actions
104
104
  end
105
105
  end
@@ -3,4 +3,4 @@
3
3
 
4
4
  ActiveAdmin::DatabaseHitDuringLoad.database_error_classes << ActiveRecord::StatementInvalid
5
5
 
6
- require "active_admin/orm/active_record/comments"
6
+ require_relative "active_record/comments"
@@ -19,11 +19,11 @@ module ActiveAdmin
19
19
  end
20
20
 
21
21
  def scope_collection(collection, action = Auth::READ)
22
- # scoping is appliable only to read/index action
22
+ # scoping is applicable only to read/index action
23
23
  # which means there is no way how to scope other actions
24
24
  Pundit.policy_scope!(user, namespace(collection))
25
25
  rescue Pundit::NotDefinedError => e
26
- if default_policy_class && default_policy_class.const_defined?(:Scope)
26
+ if default_policy_class&.const_defined?(:Scope)
27
27
  default_policy_class::Scope.new(user, collection).resolve
28
28
  else
29
29
  raise e
@@ -42,7 +42,7 @@ module ActiveAdmin
42
42
  end
43
43
 
44
44
  def format_action(action, subject)
45
- # https://github.com/varvet/pundit/blob/master/lib/generators/pundit/install/templates/application_policy.rb
45
+ # https://github.com/varvet/pundit/blob/main/lib/generators/pundit/install/templates/application_policy.rb
46
46
  case action
47
47
  when Auth::READ then subject.is_a?(Class) ? :index? : :show?
48
48
  when Auth::DESTROY then subject.is_a?(Class) ? :destroy_all? : :destroy?
@@ -95,7 +95,7 @@ module ActiveAdmin
95
95
  end
96
96
 
97
97
  def default_policy_class
98
- ActiveAdmin.application.pundit_default_policy && ActiveAdmin.application.pundit_default_policy.constantize
98
+ ActiveAdmin.application.pundit_default_policy&.constantize
99
99
  end
100
100
 
101
101
  def default_policy(subject)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require "active_admin/helpers/optional_display"
2
+ require_relative "../helpers/optional_display"
3
3
 
4
4
  module ActiveAdmin
5
5
 
@@ -59,7 +59,7 @@ module ActiveAdmin
59
59
  def add_default_action_items
60
60
  add_default_new_action_item
61
61
  add_default_edit_action_item
62
- add_default_show_action_item
62
+ add_default_destroy_action_item
63
63
  end
64
64
 
65
65
  # Adds the default New link on index
@@ -79,7 +79,7 @@ module ActiveAdmin
79
79
  end
80
80
 
81
81
  # Adds the default Destroy link on show
82
- def add_default_show_action_item
82
+ def add_default_destroy_action_item
83
83
  add_action_item :destroy, only: :show, if: -> { destroy_action_authorized?(resource) } do
84
84
  localizer = ActiveAdmin::Localizers.resource(active_admin_config)
85
85
  link_to(
@@ -37,7 +37,14 @@ module ActiveAdmin
37
37
  end
38
38
 
39
39
  def counter_cache_col?(c)
40
- c.name.end_with?("_count")
40
+ # This helper is called inside a loop. Let's memoize the result.
41
+ @counter_cache_columns ||= begin
42
+ resource_class.reflect_on_all_associations(:has_many)
43
+ .select(&:has_cached_counter?)
44
+ .map(&:counter_cache_column)
45
+ end
46
+
47
+ @counter_cache_columns.include?(c.name)
41
48
  end
42
49
 
43
50
  def filtered_col?(c)
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require "active_admin/resource"
3
2
 
4
3
  module ActiveAdmin
5
4
  class Resource
@@ -5,7 +5,7 @@ module ActiveAdmin
5
5
  module Naming
6
6
  def resource_name
7
7
  @resource_name ||= begin
8
- as = @options[:as].gsub /\s/, "" if @options[:as]
8
+ as = @options[:as].gsub(/\s/, "") if @options[:as]
9
9
 
10
10
  if as || !resource_class.respond_to?(:model_name)
11
11
  Name.new resource_class, as
@@ -51,7 +51,7 @@ module ActiveAdmin
51
51
  # Stores a config for all index actions supplied
52
52
  #
53
53
  # @param [Symbol] index_as The index type to store in the configuration
54
- # @param [PagePresenter] page_presenter The intance of PagePresenter to store
54
+ # @param [PagePresenter] page_presenter The instance of PagePresenter to store
55
55
  def set_index_presenter(index_as, page_presenter)
56
56
  page_presenters[:index] ||= {}
57
57
 
@@ -64,7 +64,7 @@ module ActiveAdmin
64
64
  page_presenters[:index][index_as] = page_presenter
65
65
  end
66
66
 
67
- # Returns the actual class for renderering the main content on the index
67
+ # Returns the actual class for rendering the main content on the index
68
68
  # page. To set this, use the :as option in the page_presenter block.
69
69
  #
70
70
  # @param [Symbol, Class] symbol_or_class The component symbol or class
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require "active_admin/helpers/optional_display"
2
+ require_relative "../helpers/optional_display"
3
3
 
4
4
  module ActiveAdmin
5
5
 
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
- require "active_admin/view_helpers/method_or_proc_helper"
3
- require "active_admin/resource/action_items"
4
- require "active_admin/resource/attributes"
5
- require "active_admin/resource/controllers"
6
- require "active_admin/resource/menu"
7
- require "active_admin/resource/page_presenters"
8
- require "active_admin/resource/pagination"
9
- require "active_admin/resource/routes"
10
- require "active_admin/resource/naming"
11
- require "active_admin/resource/scopes"
12
- require "active_admin/resource/includes"
13
- require "active_admin/resource/scope_to"
14
- require "active_admin/resource/sidebars"
15
- require "active_admin/resource/belongs_to"
16
- require "active_admin/resource/ordering"
17
- require "active_admin/resource/model"
2
+ require_relative "view_helpers/method_or_proc_helper"
3
+ require_relative "resource/action_items"
4
+ require_relative "resource/attributes"
5
+ require_relative "resource/controllers"
6
+ require_relative "resource/menu"
7
+ require_relative "resource/page_presenters"
8
+ require_relative "resource/pagination"
9
+ require_relative "resource/routes"
10
+ require_relative "resource/naming"
11
+ require_relative "resource/scopes"
12
+ require_relative "resource/includes"
13
+ require_relative "resource/scope_to"
14
+ require_relative "resource/sidebars"
15
+ require_relative "resource/belongs_to"
16
+ require_relative "resource/ordering"
17
+ require_relative "resource/model"
18
18
 
19
19
  module ActiveAdmin
20
20
 
@@ -71,7 +71,7 @@ module ActiveAdmin
71
71
  module Base
72
72
  def initialize(namespace, resource_class, options = {})
73
73
  @namespace = namespace
74
- @resource_class_name = "::#{resource_class.name}"
74
+ @resource_class_name = resource_class.respond_to?(:name) ? "::#{resource_class.name}" : resource_class.to_s
75
75
  @options = options
76
76
  @sort_order = options[:sort_order]
77
77
  @member_actions = []
@@ -120,7 +120,9 @@ module ActiveAdmin
120
120
  end
121
121
 
122
122
  def resource_quoted_column_name(column)
123
- resource_class.connection.quote_column_name(column)
123
+ resource_class.with_connection do |connection|
124
+ connection.quote_column_name(column)
125
+ end
124
126
  end
125
127
 
126
128
  # Clears all the member actions this resource knows about
@@ -177,7 +179,7 @@ module ActiveAdmin
177
179
  end
178
180
 
179
181
  def find_resource(id)
180
- resource = resource_class.public_send *method_for_find(id)
182
+ resource = resource_class.public_send(*method_for_find(id))
181
183
  (decorator_class && resource) ? decorator_class.new(resource) : resource
182
184
  end
183
185
 
@@ -22,7 +22,7 @@ module ActiveAdmin
22
22
 
23
23
  # Changes `each` to pass in the value, instead of both the key and value.
24
24
  def each(&block)
25
- values.each &block
25
+ values.each(&block)
26
26
  end
27
27
 
28
28
  def [](obj)
@@ -40,7 +40,7 @@ module ActiveAdmin
40
40
 
41
41
  # Store relations that should be included
42
42
  def includes(*args)
43
- config.includes.push *args
43
+ config.includes.push(*args)
44
44
  end
45
45
 
46
46
  #
@@ -193,7 +193,7 @@ module ActiveAdmin
193
193
 
194
194
  standard_rails_filters =
195
195
  AbstractController::Callbacks::ClassMethods.public_instance_methods.select { |m| m.end_with?('_action') }
196
- delegate *standard_rails_filters, to: :controller
196
+ delegate(*standard_rails_filters, to: :controller)
197
197
 
198
198
  # Specify which actions to create in the controller
199
199
  #
@@ -21,7 +21,7 @@ module ActiveAdmin
21
21
  if namespace.root?
22
22
  router.root namespace.root_to_options.merge(to: namespace.root_to)
23
23
  else
24
- router.namespace namespace.name, namespace.route_options.dup do
24
+ router.namespace namespace.name, **namespace.route_options.dup do
25
25
  router.root namespace.root_to_options.merge(to: namespace.root_to, as: :root)
26
26
  end
27
27
  end
@@ -65,7 +65,7 @@ module ActiveAdmin
65
65
 
66
66
  def page_routes(config)
67
67
  page = config.underscored_resource_name
68
- router.get "/#{page}" => "#{page}#index"
68
+ router.get "/#{page}", to: "#{page}#index"
69
69
  config.page_actions.each do |action|
70
70
  Array.wrap(action.http_verb).each do |verb|
71
71
  build_route(verb, "/#{page}/#{action.name}" => "#{page}##{action.name}")
@@ -91,8 +91,8 @@ module ActiveAdmin
91
91
  build_route(action.http_verb, action.name)
92
92
  end
93
93
 
94
- def build_route(verbs, *args)
95
- Array.wrap(verbs).each { |verb| router.send(verb, *args) }
94
+ def build_route(verbs, ...)
95
+ Array.wrap(verbs).each { |verb| router.send(verb, ...) }
96
96
  end
97
97
 
98
98
  def define_belongs_to_routes(config)
@@ -107,7 +107,7 @@ module ActiveAdmin
107
107
  end
108
108
 
109
109
  def define_namespace(config)
110
- router.namespace config.namespace.name, config.namespace.route_options.dup do
110
+ router.namespace config.namespace.name, **config.namespace.route_options.dup do
111
111
  define_routes(config)
112
112
  end
113
113
  end
@@ -14,6 +14,12 @@ module ActiveAdmin
14
14
  # Scope.new('Published', :public)
15
15
  # # => Scope with name 'Published' and scope method :public
16
16
  #
17
+ # Scope.new(:published, show_count: :async)
18
+ # # => Scope with name 'Published' that queries its count asynchronously
19
+ #
20
+ # Scope.new(:published, show_count: false)
21
+ # # => Scope with name 'Published' that does not display a count
22
+ #
17
23
  # Scope.new 'Published', :public, if: proc { current_admin_user.can? :manage, resource_class } do |articles|
18
24
  # articles.where published: true
19
25
  # end
@@ -61,5 +67,9 @@ module ActiveAdmin
61
67
  end
62
68
  end
63
69
 
70
+ def async_count?
71
+ @show_count == :async
72
+ end
73
+
64
74
  end
65
75
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module ActiveAdmin
3
- VERSION = "4.0.0.beta2"
3
+ VERSION = "4.0.0.beta22"
4
4
  end
@@ -91,7 +91,7 @@ module MethodOrProcHelper
91
91
  context = self if context.nil? # default to `self` only when nil
92
92
  case obj
93
93
  when Proc
94
- context.instance_exec *args, &obj
94
+ context.instance_exec(*args, &obj)
95
95
  when Symbol
96
96
  context.public_send obj, *args
97
97
  else
@@ -131,8 +131,8 @@ module ActiveAdmin
131
131
  html_options[:class] ||= "inputs"
132
132
  legend = args.shift if args.first.is_a?(::String)
133
133
  legend = html_options.delete(:name) if html_options.key?(:name)
134
- legend_tag = legend ? "<legend class=\"fieldset-title\">#{legend}</legend>" : ""
135
- fieldset_attrs = html_options.map { |k, v| %Q{#{k}="#{v}"} }.join(" ")
134
+ legend_tag = legend ? helpers.tag.legend(legend, class: "fieldset-title") : ""
135
+ fieldset_attrs = helpers.tag.attributes html_options
136
136
  @opening_tag = "<fieldset #{fieldset_attrs}>#{legend_tag}<ol>"
137
137
  @closing_tag = "</ol></fieldset>"
138
138
  super(*(args << html_options), &block)
@@ -23,6 +23,7 @@ module ActiveAdmin
23
23
 
24
24
  def row(*args, &block)
25
25
  title = args[0]
26
+ data = args[1] || args[0]
26
27
  options = args.extract_options!
27
28
  options["data-row"] = title.to_s.parameterize(separator: "_") if title.present?
28
29
 
@@ -32,7 +33,7 @@ module ActiveAdmin
32
33
  end
33
34
  @collection.each do |record|
34
35
  td do
35
- content_for(record, block || title)
36
+ content_for(record, block || data)
36
37
  end
37
38
  end
38
39
  end
@@ -60,10 +61,15 @@ module ActiveAdmin
60
61
  end
61
62
 
62
63
  def header_content_for(attr)
63
- if @resource_class.respond_to?(:human_attribute_name)
64
- @resource_class.human_attribute_name(attr, default: attr.to_s.titleize)
64
+ if attr.is_a?(Symbol)
65
+ default = attr.to_s.titleize
66
+ if @resource_class.respond_to?(:human_attribute_name)
67
+ @resource_class.human_attribute_name(attr, default: default)
68
+ else
69
+ default
70
+ end
65
71
  else
66
- attr.to_s.titleize
72
+ attr.to_s
67
73
  end
68
74
  end
69
75
 
@@ -102,7 +102,10 @@ module ActiveAdmin
102
102
  # you pass in the :total_pages option. We issue a query to determine
103
103
  # if there is another page or not, but the limit/offset make this
104
104
  # query fast.
105
- offset = @collection.offset(@collection.current_page * @collection.limit_value).limit(1).count
105
+ offset_scope = @collection.offset(@collection.current_page * @collection.limit_value)
106
+ # Support array collections. Kaminari::PaginatableArray does not respond to except
107
+ offset_scope = offset_scope.except(:select, :order) if offset_scope.respond_to?(:except)
108
+ offset = offset_scope.limit(1).count
106
109
  options[:total_pages] = @collection.current_page + offset
107
110
  options[:right] = 0
108
111
  end
@@ -22,7 +22,7 @@ module ActiveAdmin
22
22
 
23
23
  # Override children? to only report children when the panel's
24
24
  # contents have been added to. This ensures that the panel
25
- # correcly appends string values, etc.
25
+ # correctly appends string values, etc.
26
26
  def children?
27
27
  @contents.children?
28
28
  end
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
- require "active_admin/view_helpers/method_or_proc_helper"
2
+ require_relative "../../async_count"
3
+ require_relative "../../view_helpers/method_or_proc_helper"
3
4
 
4
5
  module ActiveAdmin
5
6
  module Views
6
7
  # Renders a collection of ActiveAdmin::Scope objects as a
7
- # simple list with a seperator
8
+ # simple list with a separator
8
9
  class Scopes < ActiveAdmin::Component
9
10
  include ActiveAdmin::ScopeChain
10
11
 
@@ -15,10 +16,12 @@ module ActiveAdmin
15
16
  def build(scopes, options = {})
16
17
  super({ role: "toolbar" })
17
18
  add_class "scopes"
19
+ prepare_async_counts(scopes, options)
20
+
18
21
  scopes.group_by(&:group).each do |group, group_scopes|
19
22
  div class: "index-button-group", role: "group", data: { "group": group_name(group) } do
20
23
  group_scopes.each do |scope|
21
- build_scope(scope, options) if call_method_or_exec_proc(scope.display_if_block)
24
+ build_scope(scope, options) if display_scope?(scope)
22
25
  end
23
26
 
24
27
  nil
@@ -55,12 +58,31 @@ module ActiveAdmin
55
58
 
56
59
  # Return the count for the scope passed in.
57
60
  def get_scope_count(scope)
58
- collection_size(scope_chain(scope, collection_before_scope))
61
+ chained = @async_counts[scope] || scope_chain(scope, collection_before_scope)
62
+
63
+ collection_size(chained)
59
64
  end
60
65
 
61
66
  def group_name(group)
62
67
  group.present? ? group : "default"
63
68
  end
69
+
70
+ private
71
+
72
+ def display_scope?(scope)
73
+ call_method_or_exec_proc(scope.display_if_block)
74
+ end
75
+
76
+ def prepare_async_counts(scopes, options)
77
+ @async_counts = if options[:scope_count]
78
+ scopes
79
+ .select(&:async_count?)
80
+ .select { |scope| display_scope?(scope) }
81
+ .index_with { |scope| AsyncCount.new(scope_chain(scope, collection_before_scope)) }
82
+ else
83
+ {}
84
+ end
85
+ end
64
86
  end
65
87
  end
66
88
  end
@@ -16,6 +16,9 @@ module ActiveAdmin
16
16
  @resource_class ||= @collection.klass if @collection.respond_to? :klass
17
17
 
18
18
  @columns = []
19
+ @tbody_html = options.delete(:tbody_html)
20
+ @row_html = options.delete(:row_html)
21
+ # To be deprecated, please use row_html instead.
19
22
  @row_class = options.delete(:row_class)
20
23
 
21
24
  build_table
@@ -91,10 +94,12 @@ module ActiveAdmin
91
94
  end
92
95
 
93
96
  def build_table_body
94
- @tbody = tbody do
97
+ @tbody = tbody(**(@tbody_html || {})) do
95
98
  # Build enough rows for our collection
96
99
  @collection.each do |elem|
97
- tr(id: dom_id_for(elem), class: @row_class&.call(elem))
100
+ html_options = @row_html&.call(elem) || {}
101
+ html_options.reverse_merge!(class: @row_class&.call(elem))
102
+ tr(id: dom_id_for(elem), **html_options)
98
103
  end
99
104
  end
100
105
  end
@@ -196,17 +196,25 @@ module ActiveAdmin
196
196
  # end
197
197
  # ```
198
198
  #
199
- # ## Custom row class
199
+ # ## Custom tbody HTML attributes
200
200
  #
201
- # In order to add special class to table rows pass the proc object as a `:row_class` option
202
- # of the `index` method.
201
+ # In order to add HTML attributes to the tbody use the `:tbody_html` option.
203
202
  #
204
203
  # ```ruby
205
- # index row_class: ->elem { 'active' if elem.active? } do
204
+ # index tbody_html: { class: "my-class", data: { controller: 'stimulus-controller' } } do
206
205
  # # columns
207
206
  # end
208
207
  # ```
209
208
  #
209
+ # ## Custom row HTML attributes
210
+ #
211
+ # In order to add HTML attributes to table rows, use a proc object in the `:row_html` option.
212
+ #
213
+ # ```ruby
214
+ # index row_html: ->elem { { class: ('active' if elem.active?), data: { 'element-id' => elem.id } } } do
215
+ # # columns
216
+ # end
217
+ # ```
210
218
  class IndexAsTable < ActiveAdmin::Component
211
219
  def build(page_presenter, collection)
212
220
  add_class "index-as-table"
@@ -215,6 +223,9 @@ module ActiveAdmin
215
223
  sortable: true,
216
224
  i18n: active_admin_config.resource_class,
217
225
  paginator: page_presenter[:paginator] != false,
226
+ tbody_html: page_presenter[:tbody_html],
227
+ row_html: page_presenter[:row_html],
228
+ # To be deprecated, please use row_html instead.
218
229
  row_class: page_presenter[:row_class]
219
230
  }
220
231
 
@@ -245,9 +256,14 @@ module ActiveAdmin
245
256
  end
246
257
 
247
258
  # Display a column for the id
248
- def id_column
259
+ def id_column(*args)
249
260
  raise "#{resource_class.name} has no primary_key!" unless resource_class.primary_key
250
- column(resource_class.human_attribute_name(resource_class.primary_key), sortable: resource_class.primary_key) do |resource|
261
+
262
+ options = args.extract_options!
263
+ title = args[0].presence || resource_class.human_attribute_name(resource_class.primary_key)
264
+ sortable = options.fetch(:sortable, resource_class.primary_key)
265
+
266
+ column(title, sortable: sortable) do |resource|
251
267
  if controller.action_methods.include?("show")
252
268
  link_to resource.id, resource_path(resource)
253
269
  elsif controller.action_methods.include?("edit")
data/lib/active_admin.rb CHANGED
@@ -9,6 +9,12 @@ require "formtastic_i18n"
9
9
  require "inherited_resources"
10
10
  require "arbre"
11
11
 
12
+ begin
13
+ require "importmap-rails"
14
+ rescue LoadError
15
+ # importmap-rails is optional
16
+ end
17
+
12
18
  module ActiveAdmin
13
19
 
14
20
  autoload :VERSION, "active_admin/version"
@@ -57,7 +63,6 @@ module ActiveAdmin
57
63
  end
58
64
 
59
65
  def importmap
60
- require "importmap-rails"
61
66
  @importmap ||= Importmap::Map.new
62
67
  end
63
68
 
@@ -120,12 +125,12 @@ module ActiveAdmin
120
125
  end
121
126
 
122
127
  # Require things that don't support autoload
123
- require "active_admin/engine"
124
- require "active_admin/error"
128
+ require_relative "active_admin/engine"
129
+ require_relative "active_admin/error"
125
130
 
126
131
  # Require internal plugins
127
- require "active_admin/batch_actions"
128
- require "active_admin/filters"
132
+ require_relative "active_admin/batch_actions"
133
+ require_relative "active_admin/filters"
129
134
 
130
135
  # Require ORM-specific plugins
131
- require "active_admin/orm/active_record" if defined? ActiveRecord
136
+ require_relative "active_admin/orm/active_record" if defined? ActiveRecord
data/lib/activeadmin.rb CHANGED
@@ -1,2 +1,2 @@
1
1
  # frozen_string_literal: true
2
- require "active_admin"
2
+ require_relative "active_admin"
@@ -1,3 +1,3 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
1
+ @import "tailwindcss";
2
+
3
+ @config "../../../tailwind-active_admin.config.js";
@@ -1,7 +1,10 @@
1
- const execSync = require('child_process').execSync;
2
- const activeAdminPath = execSync('bundle show activeadmin', { encoding: 'utf-8' }).trim();
1
+ import { execSync } from 'child_process';
2
+ import activeAdminPlugin from '@activeadmin/activeadmin/plugin';
3
3
 
4
- module.exports = {
4
+ // Always use the last line of output since Bundler's DEBUG env will print additional lines.
5
+ const activeAdminPath = execSync('bundle show activeadmin', { encoding: 'utf-8' }).trim().split(/\r?\n/).pop();
6
+
7
+ export default {
5
8
  content: [
6
9
  `${activeAdminPath}/vendor/javascript/flowbite.js`,
7
10
  `${activeAdminPath}/plugin.js`,
@@ -9,10 +12,11 @@ module.exports = {
9
12
  './app/admin/**/*.{arb,erb,html,rb}',
10
13
  './app/views/active_admin/**/*.{arb,erb,html,rb}',
11
14
  './app/views/admin/**/*.{arb,erb,html,rb}',
15
+ './app/views/layouts/active_admin*.{erb,html}',
12
16
  './app/javascript/**/*.js'
13
17
  ],
14
- darkMode: "class",
18
+ darkMode: "selector",
15
19
  plugins: [
16
- require(`@activeadmin/activeadmin/plugin`)
20
+ activeAdminPlugin
17
21
  ]
18
22
  }
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require "active_admin/error"
3
- require "active_admin/dependency"
2
+ require_relative "../../../active_admin/error"
3
+ require_relative "../../../active_admin/dependency"
4
4
 
5
5
  module ActiveAdmin
6
6
  module Generators
@@ -203,7 +203,7 @@ ActiveAdmin.setup do |config|
203
203
  #
204
204
  # config.namespace :admin do |admin|
205
205
  # admin.build_menu :default do |menu|
206
- # menu.add label: "My Great Website", url: "https://mygreatwebsite.example.com", html_options: { target: :blank }
206
+ # menu.add label: "My Great Website", url: "https://mygreatwebsite.example.com", html_options: { target: "_blank" }
207
207
  # end
208
208
  # end
209
209