activeadmin 4.0.0.beta8 → 4.0.0.beta10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a63b35e7f784fd50ba0471841cb52b10cf47f732c17e9a03b632303cedb922cf
4
- data.tar.gz: 54a8a264547063ddf0b1178310a4c8a00444aaa32796e4fe9abd72c04b2406aa
3
+ metadata.gz: 17abe55cddeec6028454b71ee930e2a99e83c0660303a6b901488aba36a90cda
4
+ data.tar.gz: 4ba193f3c02f03b20609553eed9e1c3788390110e02cbae5fcbc924087d71785
5
5
  SHA512:
6
- metadata.gz: 2f6a7cba813a4ce9af1f4192ab9dd96287b46044524927ab1c410875225f803ee970943658fe4942a59d6647791c61db30ab26479d87fa2db39fa92aeafb6258
7
- data.tar.gz: fe2b8edf174e2bafcc5efcf21d7cbd2f5f8aa5449aa2f66a2963f537116d41554d30cde07014230dcdfb100bd71f66071d3e96bb8a6ff487656df713c17d2693
6
+ metadata.gz: 13f5d85ad6fb9add3eb08569f09605fbc2e8b695b5598d7d41cfe6eb62536c29f6b405a87bcf724c957d7c95e893d599a6a5dc47a8318a7f58f8d0ba8a50a677
7
+ data.tar.gz: 0e82b2549cac3763a82e7910107e82fb05f600312395c1bfa2c1519969419082c19dfb2ec8329adcda7fefa0734a21c17296540604f86c0e7ee0abc9774ccf20
data/README.md CHANGED
@@ -75,8 +75,8 @@ Thanks to [Tidelift][tidelift] and all our Tidelift subscribers.
75
75
  Thanks to [Open Collective][opencollective contributors] and all our Open Collective contributors.
76
76
 
77
77
  [Arbre]: https://github.com/activeadmin/arbre
78
- [Devise]: https://github.com/plataformatec/devise
79
- [Formtastic]: https://github.com/justinfrench/formtastic
78
+ [Devise]: https://github.com/heartcombo/devise
79
+ [Formtastic]: https://github.com/formtastic/formtastic
80
80
  [Inherited Resources]: https://github.com/activeadmin/inherited_resources
81
81
  [Kaminari]: https://github.com/kaminari/kaminari
82
82
  [Ransack]: https://github.com/activerecord-hackery/ransack
@@ -92,7 +92,7 @@ Thanks to [Open Collective][opencollective contributors] and all our Open Collec
92
92
  [tidelift_enterprise]: https://tidelift.com/subscription/pkg/rubygems-activeadmin?utm_source=rubygems-activeadmin&utm_medium=referral&utm_campaign=enterprise
93
93
  [tidelift_support]: https://tidelift.com/subscription/pkg/rubygems-activeadmin?utm_source=rubygems-activeadmin&utm_medium=referral&utm_campaign=github&utm_content=support
94
94
 
95
- [docs]: https://activeadmin.info/0-installation.html
95
+ [docs]: https://activeadmin.info/
96
96
  [wiki]: https://github.com/activeadmin/activeadmin/wiki
97
97
  [stackoverflow]: https://stackoverflow.com/questions/tagged/activeadmin
98
98
  [contributing]: https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md
data/UPGRADING.md CHANGED
@@ -8,14 +8,14 @@ ActiveAdmin v4 uses TailwindCSS. It has **mobile web, dark mode and RTL support*
8
8
 
9
9
  These instructions assume the `cssbundling-rails` and `importmap-rails` gems are already installed and you have run their install commands in your app. If you haven't done so, please do before continuing.
10
10
 
11
- Update your `Gemfile` with `gem "activeadmin", "4.0.0.beta3"` and then run `gem install activeadmin --pre`.
11
+ Update your `Gemfile` with `gem "activeadmin", "4.0.0.beta10"` and then run `gem install activeadmin --pre`.
12
12
 
13
13
  Now, run `rails generate active_admin:assets` to replace the old assets with the new files.
14
14
 
15
15
  Then add the npm package and update the `build:css` script.
16
16
 
17
17
  ```
18
- yarn add @activeadmin/activeadmin@4.0.0-beta3
18
+ yarn add @activeadmin/activeadmin@4.0.0-beta10
19
19
  npm pkg set scripts.build:css="tailwindcss -i ./app/assets/stylesheets/active_admin.css -o ./app/assets/builds/active_admin.css --minify -c tailwind-active_admin.config.js"
20
20
  ```
21
21
 
@@ -224,3 +224,4 @@ This release includes several locale changes. Please [reivew the en.yml locale](
224
224
  - The `comments.title_content` text has been updated with an "All " prefix.
225
225
  - The `comments.delete_confirmation` text has been fixed to use singular form.
226
226
  - Inconsistent use of login/sign-in related terms so text now uses "Sign in", Sign out", and "Sign up" throughout.
227
+ - The `toggle_dark_mode`, `toggle_main_navigation_menu`, `toggle_section`, and `toggle_user_menu` keys have been added.
@@ -14,7 +14,7 @@ const batchActionClick = function(event) {
14
14
  batchAction.value = this.dataset.action
15
15
  }
16
16
 
17
- if (!event.target.dataset.confirm) { submitForm() }
17
+ if (!event.target.dataset.confirm && !event.target.dataset.modalTarget) { submitForm() }
18
18
  }
19
19
 
20
20
  const batchActionConfirmComplete = function(event) {
@@ -1,10 +1,10 @@
1
- <div id="main-menu" class="fixed top-0 xl:top-16 bottom-0 start-0 z-40 w-72 xl:w-60 p-4 overflow-y-auto transition-transform duration-200 -translate-x-full xl:translate-x-0 bg-white dark:bg-gray-950 xl:border-e xl:border-gray-200 xl:dark:border-white/10" tabindex="-1" aria-labelledby="drawer-navigation-label">
1
+ <div id="main-menu" class="fixed top-0 xl:top-16 bottom-0 start-0 z-40 w-72 xl:w-60 p-4 overflow-y-auto transition-transform duration-200 -translate-x-full xl:translate-x-0 bg-white dark:bg-gray-950 xl:border-e xl:border-gray-200 xl:dark:border-white/10" tabindex="-1">
2
2
  <ul role="list" class="flex flex-1 flex-col space-y-1.5">
3
3
  <% current_menu.items(self).each do |item| %>
4
4
  <% children = item.items(self).presence %>
5
5
  <li <%= current_menu_item?(item) && "data-open" %> class="group" data-item-id="<%= item.id %>">
6
6
  <% if children %>
7
- <button data-menu-button class="text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white flex items-center w-full rounded-md p-2 gap-x-2 text-sm">
7
+ <button data-menu-button class="text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white flex items-center w-full rounded-md p-2 gap-x-2 text-sm" aria-label="<%= t('active_admin.toggle_section') %>">
8
8
  <%= item.label(self) %>
9
9
  <svg class="group-data-[open]:rotate-90 group-data-[open]:rtl:-rotate-90 ms-auto h-5 w-5 shrink-0 rtl:-scale-x-100" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
10
10
  <path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
@@ -1,5 +1,5 @@
1
1
  <div class="border-b border-gray-200 dark:border-white/10 dark:bg-gray-950/75 px-4 py-2 flex items-center sticky top-0 z-20 h-16 w-full backdrop-blur-md">
2
- <button class="xl:hidden pe-3 inline-flex items-center w-8 h-8 justify-center text-sm text-gray-500 dark:text-gray-400 focus-visible:outline-none focus-visible:ring-ring focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0" data-drawer-target="main-menu" data-drawer-show="main-menu" aria-controls="drawer-navigation">
2
+ <button class="xl:hidden pe-3 inline-flex items-center w-8 h-8 justify-center text-sm text-gray-500 dark:text-gray-400 focus-visible:outline-none focus-visible:ring-ring focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0" data-drawer-target="main-menu" data-drawer-show="main-menu" aria-controls="main-menu" aria-label="<%= t('active_admin.toggle_main_navigation_menu') %>">
3
3
  <svg class="w-5 h-5 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/></svg>
4
4
  </button>
5
5
 
@@ -9,12 +9,12 @@
9
9
  </h1>
10
10
  </div>
11
11
 
12
- <button type="button" class="dark-mode-toggle flex items-center w-9 h-9 justify-center me-1 text-gray-400 hover:text-gray-500 dark:text-gray-500 dark:hover:text-gray-400 focus:outline-none text-sm">
12
+ <button type="button" class="dark-mode-toggle flex items-center w-9 h-9 justify-center me-1 text-gray-400 hover:text-gray-500 dark:text-gray-500 dark:hover:text-gray-400 focus:outline-none text-sm" aria-label="<%= t('active_admin.toggle_dark_mode') %>">
13
13
  <svg class="hidden dark:block w-5 h-5 rtl:-scale-x-100" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.509 5.75c0-1.493.394-2.96 1.144-4.25h-.081a8.5 8.5 0 1 0 7.356 12.746A8.5 8.5 0 0 1 8.509 5.75Z"/></svg>
14
14
  <svg class="dark:hidden w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 3V1m0 18v-2M5.05 5.05 3.636 3.636m12.728 12.728L14.95 14.95M3 10H1m18 0h-2M5.05 14.95l-1.414 1.414M16.364 3.636 14.95 5.05M14 10a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z"/></svg>
15
15
  </button>
16
16
 
17
- <button id="user-menu-button" class="flex items-center w-9 h-9 justify-center text-sm text-gray-500 focus:outline-none dark:text-gray-200" data-dropdown-toggle="user-menu" data-dropdown-offset-distance="3" data-dropdown-placement="bottom-end">
17
+ <button id="user-menu-button" class="flex items-center w-9 h-9 justify-center text-sm text-gray-500 focus:outline-none dark:text-gray-200" data-dropdown-toggle="user-menu" data-dropdown-offset-distance="3" data-dropdown-placement="bottom-end" aria-label="<%= t('active_admin.toggle_user_menu') %>">
18
18
  <svg class="w-7 h-7" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"><path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z"/></svg>
19
19
  </button>
20
20
 
@@ -52,6 +52,10 @@ en:
52
52
  "yes": "Yes"
53
53
  "no": "No"
54
54
  "unset": "Unknown"
55
+ toggle_dark_mode: Toggle dark mode
56
+ toggle_main_navigation_menu: Toggle main navigation menu
57
+ toggle_section: Toggle section
58
+ toggle_user_menu: Toggle user menu
55
59
  logout: "Sign out"
56
60
  powered_by: "Powered by %{active_admin} %{version}"
57
61
  sidebars:
@@ -52,6 +52,10 @@ it:
52
52
  "yes": "Sì"
53
53
  "no": "No"
54
54
  "unset": "Vuoto"
55
+ toggle_dark_mode: Attiva/Disattiva tema scuro
56
+ toggle_main_navigation_menu: Espandi/Riduci menu di navigazione principale
57
+ toggle_section: Espandi/Riduci sezione
58
+ toggle_user_menu: Espandi/Riduci menu utente
55
59
  logout: "Esci"
56
60
  powered_by: "Powered by %{active_admin} %{version}"
57
61
  sidebars:
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ module ActiveAdmin
3
+ class AsyncCount
4
+ class NotSupportedError < RuntimeError; end
5
+
6
+ def initialize(collection)
7
+ raise NotSupportedError, "#{collection.inspect} does not support :async_count" unless collection.respond_to?(:async_count)
8
+
9
+ @collection = collection.except(:select, :order)
10
+ @promise = @collection.async_count
11
+ end
12
+
13
+ def count
14
+ value = @promise.value
15
+ # value.value due to Rails bug https://github.com/rails/rails/issues/50776
16
+ value.respond_to?(:value) ? value.value : value
17
+ end
18
+
19
+ alias size count
20
+
21
+ delegate :except, :group_values, :length, :limit_value, to: :@collection
22
+ end
23
+ end
@@ -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?
@@ -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)
@@ -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.beta8"
3
+ VERSION = "4.0.0.beta10"
4
4
  end
@@ -131,12 +131,24 @@ 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\">#{ERB::Util.html_escape(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 = 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)
139
139
  end
140
+
141
+ private
142
+
143
+ def tag_attributes(html_options)
144
+ if Rails::VERSION::MAJOR <= 6
145
+ # Reimplement tag.attributes to backport support for Rails 6.1.
146
+ # TODO: this can be removed when support for Rails 6.x is dropped
147
+ helpers.tag.tag_options(html_options.to_h).to_s.strip.html_safe
148
+ else
149
+ helpers.tag.attributes html_options
150
+ end
151
+ end
140
152
  end
141
153
 
142
154
  class SemanticActionsProxy < FormtasticProxy
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "active_admin/async_count"
2
3
  require "active_admin/view_helpers/method_or_proc_helper"
3
4
 
4
5
  module ActiveAdmin
@@ -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
 
@@ -9,6 +9,7 @@ module.exports = {
9
9
  './app/admin/**/*.{arb,erb,html,rb}',
10
10
  './app/views/active_admin/**/*.{arb,erb,html,rb}',
11
11
  './app/views/admin/**/*.{arb,erb,html,rb}',
12
+ './app/views/layouts/active_admin*.{erb,html}',
12
13
  './app/javascript/**/*.js'
13
14
  ],
14
15
  darkMode: "selector",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeadmin
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0.beta8
4
+ version: 4.0.0.beta10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Maresh
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2024-07-27 00:00:00.000000000 Z
18
+ date: 2024-08-24 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: arbre
@@ -263,6 +263,7 @@ files:
263
263
  - lib/active_admin.rb
264
264
  - lib/active_admin/application.rb
265
265
  - lib/active_admin/application_settings.rb
266
+ - lib/active_admin/async_count.rb
266
267
  - lib/active_admin/authorization_adapter.rb
267
268
  - lib/active_admin/batch_actions.rb
268
269
  - lib/active_admin/batch_actions/controller.rb
@@ -383,7 +384,7 @@ licenses:
383
384
  - MIT
384
385
  metadata:
385
386
  bug_tracker_uri: https://github.com/activeadmin/activeadmin/issues
386
- changelog_uri: https://github.com/activeadmin/activeadmin/blob/master/CHANGELOG.md
387
+ changelog_uri: https://github.com/activeadmin/activeadmin/releases
387
388
  documentation_uri: https://activeadmin.info
388
389
  homepage_uri: https://activeadmin.info
389
390
  mailing_list_uri: https://groups.google.com/group/activeadmin