activeadmin_favorites 0.1.4 → 0.1.6

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: 5e9bfde6f82c66411cf985ef89e74433f9f6651726d8ae554e32c4a56852a6f0
4
- data.tar.gz: ea0ec745f66df6282044bdbea08598072e34194c2a6d78efb1629d441595b778
3
+ metadata.gz: 1d95f2a47f409353aaaa307098bf24dc0b94abc2bf116f47101259a81f8e481a
4
+ data.tar.gz: 9842f18c72d0e9fbe731b934a5d58c0eae9633eb0438849a57b8d09c10b87aaf
5
5
  SHA512:
6
- metadata.gz: b677efa90bdf4b467645703ec784c25a22d652e837b98b8daeaaa5eb755349e1040dd8f564b2025a3f6ba0d0450e34996a916ad031069f85febb3d80a2739677
7
- data.tar.gz: db2cbb3c3a772863a0a46451e6830470737e199265d8fdfc70b0fe13d7ed351b5719143746011d6ce6b55b1fe49eb817166f343fc0cd9661806034a3a0b78388
6
+ metadata.gz: 52d6fd9962c311fcee1f14c98211478337761a916eb074e83ebb641063370eb16c8b81ad921f45d59af4c8ad0b4e328fa83c6b52e9ef6423fa823ce660d1c9a5
7
+ data.tar.gz: ba88cae3210e174538ec0dd397f7085f451093e9b86c364219d4ba2453302917fbb0c7d6918843a2d83abffb8a7ea11a1dc765811f2cd160ab70188dded4dbec
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.1.6 (2026-10-27)
4
+
5
+ - Link the personalization menu stylesheet from Active Admin's HTML head so hosts no longer need a Tailwind `@import` of the Propshaft path.
6
+ - Document `ActiveAdmin::Favorites.personalization_menu_stylesheet_path` for hosts that concatenate the plain CSS into a Tailwind CLI entry.
7
+
8
+ ## 0.1.5 (2026-10-26)
9
+
10
+ - Keep personalization menu actions in view while a long catalog scrolls.
11
+ - Add `activeadmin_favorites/personalization_menu.css` for hosts to import into the ActiveAdmin stylesheet.
12
+
3
13
  ## 0.1.4 (2026-10-26)
4
14
 
5
15
  - Fix column and panel personalization so the lens catalog on index and show pages parses in the browser.
data/README.md CHANGED
@@ -59,6 +59,22 @@ 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
+ The engine links `activeadmin_favorites/personalization_menu` from Active Admin's HTML head so the cogwheel dropdown can scroll the catalog while keeping action buttons in view. The file is plain CSS served through Propshaft; it does not need Tailwind processing.
65
+
66
+ If the host overrides `active_admin/_html_head`, add the link there:
67
+
68
+ ```erb
69
+ <%= stylesheet_link_tag "activeadmin_favorites/personalization_menu" %>
70
+ ```
71
+
72
+ Do not bare-`@import` that Propshaft logical path into an Active Admin CSS entry that Tailwind CLI compiles from a generated `tmp` file. Tailwind resolves imports from the input file directory, not through Propshaft. Hosts that must fold the rules into one built stylesheet can concatenate the absolute file instead:
73
+
74
+ ```ruby
75
+ File.read(ActiveAdmin::Favorites.personalization_menu_stylesheet_path)
76
+ ```
77
+
62
78
  ### Locales
63
79
 
64
80
  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
+ }
@@ -0,0 +1,14 @@
1
+ <%= stylesheet_link_tag "active_admin" %>
2
+ <%= stylesheet_link_tag ActiveAdmin::Favorites::PERSONALIZATION_MENU_STYLESHEET %>
3
+ <meta name="viewport" content="width=device-width,initial-scale=1">
4
+ <%= csrf_meta_tags %>
5
+ <%= csp_meta_tag %>
6
+ <% # On page load or when changing themes, best to add inline in `head` to avoid FOUC %>
7
+ <%= javascript_tag nonce: true do %>
8
+ if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
9
+ document.documentElement.classList.add('dark')
10
+ } else {
11
+ document.documentElement.classList.remove('dark')
12
+ }
13
+ <% end %>
14
+ <%= javascript_importmap_tags "active_admin", importmap: ActiveAdmin.importmap %>
@@ -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>
@@ -29,6 +29,7 @@ module ActiveAdmin
29
29
  next unless app.config.respond_to?(:assets)
30
30
 
31
31
  app.config.assets.precompile << "activeadmin_favorites/personalization_controller.js"
32
+ app.config.assets.precompile << "activeadmin_favorites/personalization_menu.css"
32
33
  end
33
34
 
34
35
  initializer "activeadmin_favorites.importmap", after: :importmap do
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveAdmin
4
4
  module Favorites
5
- VERSION = "0.1.4"
5
+ VERSION = "0.1.6"
6
6
  end
7
7
  end
@@ -5,6 +5,8 @@ require "activeadmin/favorites/configuration"
5
5
 
6
6
  module ActiveAdmin
7
7
  module Favorites
8
+ PERSONALIZATION_MENU_STYLESHEET = "activeadmin_favorites/personalization_menu"
9
+
8
10
  class << self
9
11
  def config
10
12
  @config ||= Configuration.new
@@ -14,6 +16,13 @@ module ActiveAdmin
14
16
  yield config
15
17
  end
16
18
 
19
+ # Absolute path to the plain CSS file Propshaft serves. Hosts that
20
+ # concatenate into a Tailwind CLI entry can File.read this path; do not
21
+ # bare-@import the Propshaft logical path from a tmp Tailwind input.
22
+ def personalization_menu_stylesheet_path
23
+ Engine.root.join("app/assets/stylesheets/#{PERSONALIZATION_MENU_STYLESHEET}.css")
24
+ end
25
+
17
26
  def install!
18
27
  require "activeadmin/favorites/install"
19
28
  Install.call
@@ -1,5 +1,8 @@
1
1
  module ActiveAdmin
2
2
  module Favorites
3
3
  VERSION: String
4
+ PERSONALIZATION_MENU_STYLESHEET: String
5
+
6
+ def self.personalization_menu_stylesheet_path: () -> Pathname
4
7
  end
5
8
  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.4
4
+ version: 0.1.6
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
@@ -354,6 +355,7 @@ files:
354
355
  - app/models/active_admin/favorites/lens_favorite.rb
355
356
  - app/models/active_admin/favorites/view_lens/layout.rb
356
357
  - app/models/active_admin/favorites/view_lens_default.rb
358
+ - app/views/active_admin/_html_head.html.erb
357
359
  - app/views/active_admin/favorites/_lens_catalog_data.html.erb
358
360
  - app/views/active_admin/favorites/_macro_fields.html.erb
359
361
  - app/views/active_admin/favorites/_personalization_trigger.html.erb