activeadmin_favorites 0.1.3 → 0.1.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c67d1a6f05368e22d06b4b985c4357694ba08f5749c6cf267038eb10425c488
4
- data.tar.gz: b547dc6d6c7bd68827cc822d91aa7714db18263720578e27e62c036627c986b2
3
+ metadata.gz: ff5b3c593b66aca8d7ebe1bdf1997d8eef0241f5a959aaeb2df486069c220d75
4
+ data.tar.gz: b2f7a9c06c263889b6949c51eb03c4fb6f8257685c4f2eb7430e2268fcb28125
5
5
  SHA512:
6
- metadata.gz: e450a4e628623508e8a7d3901c6b2545e77090eda375d1bc8c08cad02fa0cb3354cfeec5b3667f4dc66e2f195bd7c86b1213d12e219e94cb9862bb1da55a4135
7
- data.tar.gz: b82f99015445ec3a636077b28a1699e0b26d2ed148b66141f9f249f28382f9b5b7956c6ffe1c394468e59841188001e4bbd5b73e3485eb9aae8308ccea4154c8
6
+ metadata.gz: d65424d28f1f81b94602c44b2f0fd0051c7bd38e9887fcb66beb63295b64ad25a72b2ccf0e45bd18991c4ce1ce8561ef7b5908ddb428e66beb04f5accf308ace
7
+ data.tar.gz: 8f5b9d04b0f105e46c4fb7d88b520472d26fbe699bebd4d38a4c663d839e9545ea3cd6599e4085b663b03142740a964fd89b3ea689010d65a5b576930ad6dd16
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.1.5 (2026-10-26)
4
+
5
+ - Keep personalization menu actions in view while a long catalog scrolls.
6
+ - Add `activeadmin_favorites/personalization_menu.css` for hosts to import into the ActiveAdmin stylesheet.
7
+
8
+ ## 0.1.4 (2026-10-26)
9
+
10
+ - Fix column and panel personalization so the lens catalog on index and show pages parses in the browser.
11
+
3
12
  ## 0.1.3 (2026-10-13)
4
13
 
5
14
  - Fix favorites index kind tabs so they render as links instead of raw kind list text.
data/README.md CHANGED
@@ -59,6 +59,14 @@ end
59
59
 
60
60
  When using ActionPolicy, add `save_lens_favorite?` and `reset?` to `ActiveAdmin::PagePolicy` (or equivalent) so the view lens preference page actions authorize correctly.
61
61
 
62
+ ### Stylesheet
63
+
64
+ Import the personalization menu stylesheet into the ActiveAdmin CSS entrypoint so the cogwheel dropdown can scroll the catalog while keeping action buttons in view:
65
+
66
+ ```css
67
+ @import "activeadmin_favorites/personalization_menu.css";
68
+ ```
69
+
62
70
  ### Locales
63
71
 
64
72
  English ships with the gem. Copy `config/locales/activeadmin_favorites.host.yml.example` into your locale files and translate `active_admin.favorites` keys.
@@ -58,7 +58,8 @@ export default class extends Controller {
58
58
 
59
59
  const groups = Object.keys(grouped)
60
60
  if (groups.length === 0) {
61
- this.catalogGroupsTarget.innerHTML = `<p class="text-sm text-gray-600 dark:text-gray-300">${this.escapeHtml(this.emptyCatalogLabelValue)}</p>`
61
+ this.catalogGroupsTarget.innerHTML =
62
+ `<p class="favorites-personalization-menu__empty">${this.escapeHtml(this.emptyCatalogLabelValue)}</p>`
62
63
  return
63
64
  }
64
65
 
@@ -70,11 +71,11 @@ export default class extends Controller {
70
71
  const entryId = this.escapeHtml(entry.id)
71
72
  return `
72
73
  <li>
73
- <label class="inline-flex items-center gap-2 text-sm">
74
+ <label class="favorites-personalization-menu__item">
74
75
  <input
75
76
  type="checkbox"
76
77
  checked
77
- class="rounded border-gray-300 dark:border-white/20"
78
+ class="favorites-personalization-menu__checkbox"
78
79
  data-group="${entryGroup}"
79
80
  data-entry-id="${entryId}"
80
81
  data-${this.identifier}-target="visibilityCheckbox"
@@ -86,9 +87,9 @@ export default class extends Controller {
86
87
  }).join("")
87
88
 
88
89
  return `
89
- <div class="mb-4">
90
- <h4 class="text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400 mb-2">${label}</h4>
91
- <ul class="space-y-2 max-h-40 overflow-y-auto">${items}</ul>
90
+ <div class="favorites-personalization-menu__group">
91
+ <h4 class="favorites-personalization-menu__group-title">${label}</h4>
92
+ <ul class="favorites-personalization-menu__list">${items}</ul>
92
93
  </div>
93
94
  `
94
95
  }).join("")
@@ -0,0 +1,145 @@
1
+ /* Catalog scrolls; action buttons stay visible inside the menu. */
2
+ .favorites-personalization-menu__body {
3
+ display: flex;
4
+ flex-direction: column;
5
+ max-height: min(70vh, calc(100dvh - 5rem));
6
+ padding: 0.625rem 0.75rem;
7
+ }
8
+
9
+ .favorites-personalization-menu__title {
10
+ flex-shrink: 0;
11
+ margin: 0 0 0.5rem;
12
+ font-size: 0.8125rem;
13
+ font-weight: 600;
14
+ line-height: 1.25;
15
+ color: var(--color-gray-800, #1f2937);
16
+ }
17
+
18
+ .dark .favorites-personalization-menu__title {
19
+ color: var(--color-gray-100, #f3f4f6);
20
+ }
21
+
22
+ .favorites-personalization-menu__catalog {
23
+ flex: 1 1 auto;
24
+ min-height: 0;
25
+ overflow-x: hidden;
26
+ overflow-y: auto;
27
+ overscroll-behavior: contain;
28
+ padding-right: 0.125rem;
29
+ }
30
+
31
+ .favorites-personalization-menu__empty {
32
+ margin: 0;
33
+ font-size: 0.75rem;
34
+ line-height: 1.35;
35
+ color: var(--color-gray-500, #6b7280);
36
+ }
37
+
38
+ .favorites-personalization-menu__group {
39
+ margin-bottom: 0.5rem;
40
+ }
41
+
42
+ .favorites-personalization-menu__group:last-child {
43
+ margin-bottom: 0;
44
+ }
45
+
46
+ .favorites-personalization-menu__group-title {
47
+ margin: 0 0 0.25rem;
48
+ font-size: 0.625rem;
49
+ font-weight: 600;
50
+ letter-spacing: 0.04em;
51
+ text-transform: uppercase;
52
+ color: var(--color-gray-500, #6b7280);
53
+ }
54
+
55
+ .favorites-personalization-menu__list {
56
+ margin: 0;
57
+ padding: 0;
58
+ list-style: none;
59
+ }
60
+
61
+ .favorites-personalization-menu__list > li + li {
62
+ margin-top: 0.125rem;
63
+ }
64
+
65
+ .favorites-personalization-menu__item {
66
+ display: inline-flex;
67
+ align-items: flex-start;
68
+ gap: 0.375rem;
69
+ font-size: 0.75rem;
70
+ line-height: 1.3;
71
+ color: var(--color-gray-800, #1f2937);
72
+ }
73
+
74
+ .dark .favorites-personalization-menu__item {
75
+ color: var(--color-gray-100, #f3f4f6);
76
+ }
77
+
78
+ .favorites-personalization-menu__checkbox {
79
+ margin-top: 0.1rem;
80
+ border-radius: 0.25rem;
81
+ border-color: var(--color-gray-300, #d1d5db);
82
+ }
83
+
84
+ .favorites-personalization-menu__actions {
85
+ flex-shrink: 0;
86
+ display: grid;
87
+ grid-template-columns: 1fr 1fr;
88
+ gap: 0.375rem;
89
+ margin-top: 0.5rem;
90
+ padding-top: 0.5rem;
91
+ border-top: 1px solid var(--color-gray-200, #e5e7eb);
92
+ }
93
+
94
+ .dark .favorites-personalization-menu__actions {
95
+ border-top-color: var(--color-gray-700, #374151);
96
+ }
97
+
98
+ .favorites-personalization-menu__action {
99
+ display: inline-flex;
100
+ align-items: center;
101
+ justify-content: center;
102
+ min-height: 1.75rem;
103
+ padding: 0.25rem 0.5rem;
104
+ border: 1px solid var(--color-gray-300, #d1d5db);
105
+ border-radius: 0.375rem;
106
+ background: var(--color-gray-50, #f9fafb);
107
+ color: var(--color-gray-800, #1f2937);
108
+ font-size: 0.6875rem;
109
+ font-weight: 500;
110
+ line-height: 1.2;
111
+ text-align: center;
112
+ cursor: pointer;
113
+ }
114
+
115
+ .favorites-personalization-menu__action:hover {
116
+ background: var(--color-gray-100, #f3f4f6);
117
+ }
118
+
119
+ .dark .favorites-personalization-menu__action {
120
+ border-color: var(--color-gray-600, #4b5563);
121
+ background: var(--color-gray-900, #111827);
122
+ color: var(--color-gray-100, #f3f4f6);
123
+ }
124
+
125
+ .dark .favorites-personalization-menu__action:hover {
126
+ background: var(--color-gray-800, #1f2937);
127
+ }
128
+
129
+ .favorites-personalization-menu__action--wide,
130
+ .favorites-personalization-menu__link {
131
+ grid-column: 1 / -1;
132
+ }
133
+
134
+ .favorites-personalization-menu__link {
135
+ display: block;
136
+ padding-top: 0.125rem;
137
+ font-size: 0.6875rem;
138
+ text-align: center;
139
+ color: var(--color-blue-500, #3b82f6);
140
+ text-decoration: none;
141
+ }
142
+
143
+ .favorites-personalization-menu__link:hover {
144
+ text-decoration: underline;
145
+ }
@@ -6,4 +6,4 @@
6
6
  <script
7
7
  type="application/json"
8
8
  id="favorites-lens-catalog-<%= active_admin_config.resource_name.route_key %>-<%= action_name %>"
9
- ><%= catalog_payload.to_json %></script>
9
+ ><%= raw json_escape(catalog_payload.to_json) %></script>
@@ -4,6 +4,7 @@
4
4
  resource_key = active_admin_config.resource_name.route_key
5
5
  hidden = layout.layout.fetch("hidden", {})
6
6
  catalog_element_id = "favorites-lens-catalog-#{resource_key}-#{action_name}"
7
+ menu_id = "personalization-menu-#{resource_key}-#{action_name}"
7
8
  %>
8
9
  <div
9
10
  class="relative"
@@ -30,7 +31,7 @@
30
31
  type="button"
31
32
  class="action-item-button inline-flex items-center gap-2"
32
33
  aria-label="<%= t("active_admin.favorites.personalize_page") %>"
33
- data-dropdown-toggle="personalization-menu-<%= resource_key %>-<%= action_name %>"
34
+ data-dropdown-toggle="<%= menu_id %>"
34
35
  data-dropdown-offset-distance="3"
35
36
  data-dropdown-placement="bottom-end"
36
37
  data-action="click-><%= stimulus_controller %>#prepareMenu"
@@ -43,27 +44,50 @@
43
44
  </button>
44
45
 
45
46
  <div
46
- id="personalization-menu-<%= resource_key %>-<%= action_name %>"
47
- class="z-50 hidden w-80 max-w-[calc(100vw-2rem)] rounded-md bg-white p-4 shadow-lg ring-1 ring-black/5 dark:bg-gray-800 dark:ring-white/10"
47
+ id="<%= menu_id %>"
48
+ class="favorites-personalization-menu z-50 hidden w-72 max-w-[calc(100vw-2rem)] overflow-hidden rounded-md bg-white shadow-lg ring-1 ring-black/5 dark:bg-gray-800 dark:ring-white/10"
48
49
  role="region"
49
50
  aria-label="<%= t("active_admin.favorites.personalization_region") %>"
50
51
  data-<%= stimulus_controller %>-target="menu"
51
52
  >
52
- <h3 class="text-sm font-semibold mb-3"><%= t("active_admin.favorites.personalization_region") %></h3>
53
+ <div class="favorites-personalization-menu__body">
54
+ <h3 class="favorites-personalization-menu__title">
55
+ <%= t("active_admin.favorites.personalization_region") %>
56
+ </h3>
53
57
 
54
- <div data-<%= stimulus_controller %>-target="catalogGroups"></div>
58
+ <div
59
+ class="favorites-personalization-menu__catalog"
60
+ data-<%= stimulus_controller %>-target="catalogGroups"
61
+ ></div>
55
62
 
56
- <div class="flex flex-col gap-2 border-t border-gray-200 pt-3 dark:border-white/10">
57
- <button type="button" class="action-item-button w-full" data-action="<%= stimulus_controller %>#saveDefault">
58
- <%= t("active_admin.favorites.save_default") %>
59
- </button>
60
- <button type="button" class="action-item-button w-full" data-action="<%= stimulus_controller %>#resetDefault">
61
- <%= t("active_admin.favorites.reset_default") %>
62
- </button>
63
- <button type="button" class="action-item-button w-full" data-action="<%= stimulus_controller %>#saveLensFavorite">
64
- <%= t("active_admin.favorites.save_lens_favorite") %>
65
- </button>
66
- <%= link_to t("active_admin.favorites.menu"), admin_favorites_path, class: "text-sm text-center no-underline hover:underline" %>
63
+ <div class="favorites-personalization-menu__actions">
64
+ <button
65
+ type="button"
66
+ class="favorites-personalization-menu__action"
67
+ data-action="<%= stimulus_controller %>#saveDefault"
68
+ >
69
+ <%= t("active_admin.favorites.save_default") %>
70
+ </button>
71
+ <button
72
+ type="button"
73
+ class="favorites-personalization-menu__action"
74
+ data-action="<%= stimulus_controller %>#resetDefault"
75
+ >
76
+ <%= t("active_admin.favorites.reset_default") %>
77
+ </button>
78
+ <button
79
+ type="button"
80
+ class="favorites-personalization-menu__action favorites-personalization-menu__action--wide"
81
+ data-action="<%= stimulus_controller %>#saveLensFavorite"
82
+ >
83
+ <%= t("active_admin.favorites.save_lens_favorite") %>
84
+ </button>
85
+ <%=
86
+ link_to t("active_admin.favorites.menu"),
87
+ admin_favorites_path,
88
+ class: "favorites-personalization-menu__link"
89
+ %>
90
+ </div>
67
91
  </div>
68
92
  </div>
69
93
  </div>
@@ -16,6 +16,7 @@ module ActiveAdmin
16
16
  require "activeadmin/favorites/view_lens/catalog"
17
17
  require "activeadmin/favorites/view_lens/patches"
18
18
  require "activeadmin/favorites/view_lens/resource_dsl"
19
+ require "activeadmin/favorites/view_paths"
19
20
  require "activeadmin/favorites/install"
20
21
  require "activeadmin/favorites/register_favorites"
21
22
  require "activeadmin/favorites/register_view_lens_preferences"
@@ -28,6 +29,7 @@ module ActiveAdmin
28
29
  next unless app.config.respond_to?(:assets)
29
30
 
30
31
  app.config.assets.precompile << "activeadmin_favorites/personalization_controller.js"
32
+ app.config.assets.precompile << "activeadmin_favorites/personalization_menu.css"
31
33
  end
32
34
 
33
35
  initializer "activeadmin_favorites.importmap", after: :importmap do
@@ -62,7 +64,13 @@ module ActiveAdmin
62
64
  end
63
65
  end
64
66
 
67
+ config.after_initialize do
68
+ ActiveAdmin::Favorites::ViewPaths.install!
69
+ end
70
+
65
71
  config.to_prepare do
72
+ ActiveAdmin::Favorites::ViewPaths.install!
73
+
66
74
  next unless defined?(ActiveAdmin::ResourceController)
67
75
 
68
76
  ActiveAdmin::Favorites::Favorite.define_favorites_user_association!
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveAdmin
4
4
  module Favorites
5
- VERSION = "0.1.3"
5
+ VERSION = "0.1.5"
6
6
  end
7
7
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAdmin
4
+ module Favorites
5
+ # Place this engine's views ahead of ActiveAdmin's so resource index/show
6
+ # overrides (catalog injection) win, while the host app/views path stays first.
7
+ module ViewPaths
8
+ module_function
9
+
10
+ def prefer_over_active_admin!(view_paths)
11
+ path_strings = view_paths.to_a.map { |path| path.to_s }
12
+ reordered = reorder_path_strings(path_strings)
13
+ return view_paths if reordered.equal?(path_strings)
14
+
15
+ ActionView::PathSet.new(reordered)
16
+ end
17
+
18
+ def install!
19
+ return unless defined?(ActionController::Base)
20
+
21
+ ActionController::Base.view_paths = prefer_over_active_admin!(
22
+ ActionController::Base.view_paths
23
+ )
24
+ end
25
+
26
+ def reorder_path_strings(path_strings)
27
+ favorites_root = Engine.root.join("app/views").to_s
28
+ return path_strings unless path_strings.include?(favorites_root)
29
+
30
+ active_admin_root = path_strings.find do |path|
31
+ path.match?(%r{/gems/activeadmin-[^/]+/app/views\z})
32
+ end
33
+ return path_strings unless active_admin_root
34
+
35
+ favorites_index = path_strings.index(favorites_root)
36
+ active_admin_index = path_strings.index(active_admin_root)
37
+ return path_strings if favorites_index < active_admin_index
38
+
39
+ moved = path_strings.dup
40
+ moved.delete_at(favorites_index)
41
+ moved.insert(moved.index(active_admin_root), favorites_root)
42
+ moved
43
+ end
44
+ module_function :reorder_path_strings
45
+ private_class_method :reorder_path_strings
46
+ end
47
+ end
48
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeadmin_favorites
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Makarov
@@ -340,6 +340,7 @@ files:
340
340
  - SECURITY.md
341
341
  - activeadmin_favorites.gemspec
342
342
  - app/assets/controllers/activeadmin_favorites/personalization_controller.js
343
+ - app/assets/stylesheets/activeadmin_favorites/personalization_menu.css
343
344
  - app/models/active_admin/favorites.rb
344
345
  - app/models/active_admin/favorites/application_record.rb
345
346
  - app/models/active_admin/favorites/combined_favorite.rb
@@ -381,6 +382,7 @@ files:
381
382
  - lib/activeadmin/favorites/view_lens/current.rb
382
383
  - lib/activeadmin/favorites/view_lens/patches.rb
383
384
  - lib/activeadmin/favorites/view_lens/resource_dsl.rb
385
+ - lib/activeadmin/favorites/view_paths.rb
384
386
  - lib/activeadmin_favorites.rb
385
387
  - lib/generators/activeadmin_favorites/install/templates/favorite_policy.rb
386
388
  - lib/generators/activeadmin_favorites/install/templates/initializer.rb