active_admin_prism 0.1.1 → 0.1.3
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 +4 -4
- data/CHANGELOG.md +121 -0
- data/INTEGRATION.md +324 -14
- data/README.md +49 -0
- data/app/assets/javascripts/active_admin_prism/prism.js +3 -1
- data/app/assets/stylesheets/active_admin_prism/prism.css +1 -1
- data/app/views/layouts/active_admin_logged_out.html.erb +76 -0
- data/js-src/prism.js +141 -6
- data/lib/active_admin/views/active_filters_bar.rb +94 -0
- data/lib/active_admin/views/flash_messages.rb +45 -25
- data/lib/active_admin/views/prism_sidebar.rb +35 -3
- data/lib/active_admin_prism/configuration.rb +53 -0
- data/lib/active_admin_prism/version.rb +1 -1
- data/lib/active_admin_prism.rb +1 -0
- data/lib/prism_icons.rb +26 -1
- data/scss-src/_base.scss +161 -24
- data/scss-src/_forms.scss +35 -4
- data/scss-src/_login.scss +5 -4
- data/scss-src/_panels.scss +172 -0
- data/scss-src/_select2.scss +281 -0
- data/scss-src/_select2_vendor.scss +695 -0
- data/scss-src/_sidebar.scss +145 -4
- data/scss-src/_variables.scss +3 -0
- data/scss-src/prism.scss +2 -0
- data/vendor/select2/LICENSE.md +21 -0
- data/vendor/select2/select2.full.min.js +2 -0
- metadata +9 -6
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<%# Overrides ActiveAdmin's own layouts/active_admin_logged_out.html.erb
|
|
2
|
+
(used for every Devise page — sign in, sign up, forgot/reset password,
|
|
3
|
+
resend confirmation, resend unlock) purely to upgrade its flash
|
|
4
|
+
rendering to this gem's toast cards (icon + dismiss button + countdown
|
|
5
|
+
bar), matching the same markup lib/active_admin/views/flash_messages.rb
|
|
6
|
+
renders for logged-in pages. Devise's pages route through this plain
|
|
7
|
+
ERB layout instead of ActiveAdmin::Views::Pages::Base/Arbre at all, so
|
|
8
|
+
that Ruby-side override alone never reaches them — this file is the
|
|
9
|
+
only way to reach them. Everything else here is copied verbatim from
|
|
10
|
+
ActiveAdmin core's own version; only the ".flashes" block differs. %>
|
|
11
|
+
<!DOCTYPE html>
|
|
12
|
+
<html lang="<%=I18n.locale%>">
|
|
13
|
+
<head>
|
|
14
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
|
15
|
+
|
|
16
|
+
<title><%= [@page_title, ActiveAdmin.application.site_title(self)].compact.join(" | ") %></title>
|
|
17
|
+
|
|
18
|
+
<% ActiveAdmin.application.stylesheets.each do |style, options| %>
|
|
19
|
+
<% if ActiveAdmin.application.use_webpacker %>
|
|
20
|
+
<%= stylesheet_pack_tag style, **options %>
|
|
21
|
+
<% else %>
|
|
22
|
+
<%= stylesheet_link_tag style, **options %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
25
|
+
<% ActiveAdmin.application.javascripts.each do |path, options| %>
|
|
26
|
+
<% if ActiveAdmin.application.use_webpacker %>
|
|
27
|
+
<%= javascript_pack_tag path, **options %>
|
|
28
|
+
<% else %>
|
|
29
|
+
<%= javascript_include_tag path, **options %>
|
|
30
|
+
<% end %>
|
|
31
|
+
<% end %>
|
|
32
|
+
|
|
33
|
+
<%= favicon_link_tag ActiveAdmin.application.favicon if ActiveAdmin.application.favicon %>
|
|
34
|
+
|
|
35
|
+
<% ActiveAdmin.application.meta_tags_for_logged_out_pages.each do |name, content| %>
|
|
36
|
+
<%= tag(:meta, name: name, content: content) %>
|
|
37
|
+
<% end %>
|
|
38
|
+
|
|
39
|
+
<%= csrf_meta_tags %>
|
|
40
|
+
<%= csp_meta_tag %>
|
|
41
|
+
</head>
|
|
42
|
+
<body class="active_admin logged_out <%= controller.action_name %>">
|
|
43
|
+
<div id="wrapper">
|
|
44
|
+
|
|
45
|
+
<div id="content_wrapper">
|
|
46
|
+
<% prism_config = ActiveAdminPrism.configuration %>
|
|
47
|
+
<% prism_auto_dismiss_ms = prism_config.flash_auto_dismiss_ms %>
|
|
48
|
+
<div class="flashes"
|
|
49
|
+
data-prism-transition-ms="<%= prism_config.flash_transition_ms.to_i %>"
|
|
50
|
+
<%= "data-prism-auto-dismiss-ms=\"#{prism_auto_dismiss_ms}\"".html_safe if prism_auto_dismiss_ms %>>
|
|
51
|
+
<% flash_messages.each do |type, message| %>
|
|
52
|
+
<div class="flash flash_<%= type %>"
|
|
53
|
+
<%= "style=\"--prism-flash-duration: #{prism_auto_dismiss_ms}ms\"".html_safe if prism_auto_dismiss_ms %>>
|
|
54
|
+
<%= ActiveAdminPrism::Icons.svg(ActiveAdminPrism::Icons.flash_icon_name(type), css_class: "prism-flash-icon")&.html_safe %>
|
|
55
|
+
<span class="prism-flash-message"><%= message %></span>
|
|
56
|
+
<% if prism_config.flash_dismissible %>
|
|
57
|
+
<button class="prism-flash-dismiss" type="button" aria-label="Dismiss" data-prism-flash-dismiss="true">
|
|
58
|
+
<%= ActiveAdminPrism::Icons.svg(:x, css_class: "prism-flash-dismiss-icon", size: 14)&.html_safe %>
|
|
59
|
+
</button>
|
|
60
|
+
<% end %>
|
|
61
|
+
<% if prism_auto_dismiss_ms %>
|
|
62
|
+
<div class="prism-flash-progress"></div>
|
|
63
|
+
<% end %>
|
|
64
|
+
</div>
|
|
65
|
+
<% end %>
|
|
66
|
+
</div>
|
|
67
|
+
<div id="active_admin_content">
|
|
68
|
+
<%= yield %>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<div id="footer">
|
|
72
|
+
<%= yield :footer %>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</body>
|
|
76
|
+
</html>
|
data/js-src/prism.js
CHANGED
|
@@ -75,6 +75,55 @@
|
|
|
75
75
|
});
|
|
76
76
|
})();
|
|
77
77
|
|
|
78
|
+
// Desktop sidebar rail-collapse toggle (ActiveAdminPrism::Configuration
|
|
79
|
+
// #sidebar_collapsible) — separate from the mobile off-canvas toggle above:
|
|
80
|
+
// that one slides the whole sidebar on/off screen below 900px; this one
|
|
81
|
+
// shrinks it to a narrow icon-only rail on desktop instead, and the two
|
|
82
|
+
// don't interact (scss-src/_sidebar.scss scopes rail-collapse to nothing in
|
|
83
|
+
// particular, but the mobile media query's own `transform`/`position: fixed`
|
|
84
|
+
// rules take over below 900px regardless of this class). State persists to
|
|
85
|
+
// localStorage under its own key, same convention as the nav-group state
|
|
86
|
+
// above, so a returning visitor keeps their last choice.
|
|
87
|
+
(function () {
|
|
88
|
+
"use strict";
|
|
89
|
+
|
|
90
|
+
var STORAGE_KEY = "prism_sidebar_collapsed";
|
|
91
|
+
|
|
92
|
+
function setCollapsed(toggle, collapsed) {
|
|
93
|
+
document.body.classList.toggle("prism-sidebar-collapsed", collapsed);
|
|
94
|
+
toggle.setAttribute("aria-expanded", collapsed ? "false" : "true");
|
|
95
|
+
toggle.setAttribute("aria-label", collapsed ? "Expand sidebar" : "Collapse sidebar");
|
|
96
|
+
try {
|
|
97
|
+
window.localStorage.setItem(STORAGE_KEY, collapsed ? "1" : "0");
|
|
98
|
+
} catch (e) {
|
|
99
|
+
// localStorage unavailable — degrade to in-memory only, no
|
|
100
|
+
// persistence across reloads.
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
105
|
+
var toggle = document.querySelector("[data-prism-toggle-sidebar-collapse]");
|
|
106
|
+
if (!toggle) return;
|
|
107
|
+
|
|
108
|
+
var stored;
|
|
109
|
+
try {
|
|
110
|
+
stored = window.localStorage.getItem(STORAGE_KEY);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
stored = null;
|
|
113
|
+
}
|
|
114
|
+
if (stored === "1") setCollapsed(toggle, true);
|
|
115
|
+
|
|
116
|
+
toggle.addEventListener("click", function () {
|
|
117
|
+
setCollapsed(toggle, !document.body.classList.contains("prism-sidebar-collapsed"));
|
|
118
|
+
});
|
|
119
|
+
toggle.addEventListener("keydown", function (event) {
|
|
120
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
121
|
+
event.preventDefault();
|
|
122
|
+
setCollapsed(toggle, !document.body.classList.contains("prism-sidebar-collapsed"));
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
})();
|
|
126
|
+
|
|
78
127
|
// Sidebar menu search box (lib/active_admin/views/prism_sidebar.rb
|
|
79
128
|
// #render_search_box) — filters the "Pages" nav as the visitor types,
|
|
80
129
|
// matching an item's own label at ANY nesting depth, not just top-level
|
|
@@ -222,7 +271,9 @@
|
|
|
222
271
|
});
|
|
223
272
|
})();
|
|
224
273
|
|
|
225
|
-
// Flash messages: dismiss button + auto-hide
|
|
274
|
+
// Flash messages: dismiss button + auto-hide, toast-style (see
|
|
275
|
+
// scss-src/_base.scss for the floating card layout and the
|
|
276
|
+
// ".prism-flash-progress" countdown bar this keeps in sync with). See
|
|
226
277
|
// lib/active_admin/views/flash_messages.rb, which renders the
|
|
227
278
|
// ".prism-flash-dismiss" button markup this responds to, and two data
|
|
228
279
|
// attributes this reads at runtime so Ruby-side config controls behavior
|
|
@@ -249,6 +300,36 @@
|
|
|
249
300
|
}, transitionMs);
|
|
250
301
|
}
|
|
251
302
|
|
|
303
|
+
// Hovering a toast pauses both its countdown bar (CSS
|
|
304
|
+
// "animation-play-state: paused" on :hover, in scss-src/_base.scss) and
|
|
305
|
+
// the actual removal timer here, tracking whatever time was left rather
|
|
306
|
+
// than restarting from the full duration — a flash a visitor is
|
|
307
|
+
// mid-read shouldn't silently vanish out from under them just because
|
|
308
|
+
// its timer happened to elapse while their mouse was still over it.
|
|
309
|
+
function armAutoDismiss(flash, totalMs, transitionMs) {
|
|
310
|
+
var remainingMs = totalMs;
|
|
311
|
+
var timer = null;
|
|
312
|
+
var startedAt = null;
|
|
313
|
+
|
|
314
|
+
function start() {
|
|
315
|
+
startedAt = Date.now();
|
|
316
|
+
timer = setTimeout(function () {
|
|
317
|
+
dismiss(flash, transitionMs);
|
|
318
|
+
}, remainingMs);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function pause() {
|
|
322
|
+
if (!timer) return;
|
|
323
|
+
clearTimeout(timer);
|
|
324
|
+
timer = null;
|
|
325
|
+
remainingMs -= Date.now() - startedAt;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
flash.addEventListener("mouseenter", pause);
|
|
329
|
+
flash.addEventListener("mouseleave", start);
|
|
330
|
+
start();
|
|
331
|
+
}
|
|
332
|
+
|
|
252
333
|
document.addEventListener("DOMContentLoaded", function () {
|
|
253
334
|
var flashesWrapper = document.querySelector(".flashes");
|
|
254
335
|
var flashes = document.querySelectorAll(".flashes .flash");
|
|
@@ -259,9 +340,7 @@
|
|
|
259
340
|
|
|
260
341
|
if (autoDismissMs) {
|
|
261
342
|
flashes.forEach(function (flash) {
|
|
262
|
-
|
|
263
|
-
dismiss(flash, transitionMs);
|
|
264
|
-
}, autoDismissMs);
|
|
343
|
+
armAutoDismiss(flash, autoDismissMs, transitionMs);
|
|
265
344
|
});
|
|
266
345
|
}
|
|
267
346
|
|
|
@@ -301,18 +380,46 @@
|
|
|
301
380
|
// #main_content/#sidebar width reflow off this body class instead.
|
|
302
381
|
document.body.classList.toggle("prism-filters-open", section.classList.contains("open"));
|
|
303
382
|
|
|
304
|
-
function
|
|
305
|
-
|
|
383
|
+
function setOpen(open) {
|
|
384
|
+
section.classList.toggle("open", open);
|
|
306
385
|
toggle.setAttribute("aria-expanded", open ? "true" : "false");
|
|
307
386
|
document.body.classList.toggle("prism-filters-open", open);
|
|
308
387
|
}
|
|
309
388
|
|
|
389
|
+
function toggleOpen() {
|
|
390
|
+
setOpen(!section.classList.contains("open"));
|
|
391
|
+
}
|
|
392
|
+
|
|
310
393
|
toggle.addEventListener("click", toggleOpen);
|
|
311
394
|
toggle.addEventListener("keydown", function (event) {
|
|
312
395
|
if (event.key !== "Enter" && event.key !== " ") return;
|
|
313
396
|
event.preventDefault();
|
|
314
397
|
toggleOpen();
|
|
315
398
|
});
|
|
399
|
+
|
|
400
|
+
// The active-filters bar (see lib/active_admin/views/active_filters_bar.rb,
|
|
401
|
+
// ActiveAdminPrism::Configuration#active_filters_bar) is a shortcut
|
|
402
|
+
// straight to the Filters form itself — clicking anywhere on it always
|
|
403
|
+
// *opens* the panel (never toggles it closed; that's still the
|
|
404
|
+
// funnel icon's job) and scrolls it into view, since #sidebar isn't
|
|
405
|
+
// sticky the way the left nav is and can scroll out of view on a
|
|
406
|
+
// long table.
|
|
407
|
+
var activeFiltersBar = document.getElementById("prism_active_filters_bar");
|
|
408
|
+
if (activeFiltersBar) {
|
|
409
|
+
activeFiltersBar.setAttribute("role", "button");
|
|
410
|
+
activeFiltersBar.setAttribute("tabindex", "0");
|
|
411
|
+
activeFiltersBar.setAttribute("aria-label", "Show filters");
|
|
412
|
+
|
|
413
|
+
var openFromBar = function (event) {
|
|
414
|
+
if (event.type === "keydown" && event.key !== "Enter" && event.key !== " ") return;
|
|
415
|
+
if (event.type === "keydown") event.preventDefault();
|
|
416
|
+
setOpen(true);
|
|
417
|
+
section.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
activeFiltersBar.addEventListener("click", openFromBar);
|
|
421
|
+
activeFiltersBar.addEventListener("keydown", openFromBar);
|
|
422
|
+
}
|
|
316
423
|
});
|
|
317
424
|
})();
|
|
318
425
|
|
|
@@ -365,3 +472,31 @@
|
|
|
365
472
|
});
|
|
366
473
|
});
|
|
367
474
|
})();
|
|
475
|
+
|
|
476
|
+
// Auto-enhances every plain <select> ActiveAdmin renders (filters, form
|
|
477
|
+
// inputs, association pickers) into a searchable Select2 widget when
|
|
478
|
+
// ActiveAdminPrism.configuration.select2 is true (the
|
|
479
|
+
// "prism-select2-enabled" body class, set in
|
|
480
|
+
// lib/active_admin/views/flash_messages.rb#body_classes). Select2 itself
|
|
481
|
+
// (vendor/select2/select2.full.min.js, MIT licensed) is concatenated just
|
|
482
|
+
// above this file in the compiled asset (see bin/build-js) — turning this
|
|
483
|
+
// flag on needs no host-side JS/npm setup of its own. Skips any <select>
|
|
484
|
+
// a host already initialized manually (the per-field
|
|
485
|
+
// input_html: { class: "..." } + own .select2() call approach predating
|
|
486
|
+
// this flag, still documented in INTEGRATION.md) so the two can coexist
|
|
487
|
+
// without double-initializing the same element.
|
|
488
|
+
(function () {
|
|
489
|
+
"use strict";
|
|
490
|
+
|
|
491
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
492
|
+
if (!document.body.classList.contains("prism-select2-enabled")) return;
|
|
493
|
+
if (typeof jQuery === "undefined" || !jQuery.fn.select2) return;
|
|
494
|
+
|
|
495
|
+
var $ = jQuery;
|
|
496
|
+
$("select").each(function () {
|
|
497
|
+
var $select = $(this);
|
|
498
|
+
if ($select.data("select2")) return;
|
|
499
|
+
$select.select2({ width: "100%" });
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
})();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAdmin
|
|
4
|
+
module Filters
|
|
5
|
+
# ActiveAdmin core auto-registers this sidebar section on every resource
|
|
6
|
+
# (see active_admin/filters/resource_extension.rb#add_search_status_sidebar_section)
|
|
7
|
+
# to summarize the current scope + active filters inside #sidebar,
|
|
8
|
+
# alongside the Filters form. Skip it entirely when
|
|
9
|
+
# ActiveAdminPrism.configuration.active_filters_bar is on — see
|
|
10
|
+
# active_filters_bar.rb's Pages::Index override, which renders the same
|
|
11
|
+
# content next to Batch Actions instead.
|
|
12
|
+
class ActiveSidebar
|
|
13
|
+
def sidebar_options
|
|
14
|
+
{
|
|
15
|
+
only: :index,
|
|
16
|
+
if: -> {
|
|
17
|
+
active_admin_config.current_filters_enabled? &&
|
|
18
|
+
(params[:q] || params[:scope]) &&
|
|
19
|
+
!ActiveAdminPrism.configuration.active_filters_bar
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module Views
|
|
27
|
+
module Pages
|
|
28
|
+
# Relocates the "Search status" summary (current scope + active
|
|
29
|
+
# filters — AA core's ActiveAdmin::Filters::ActiveSidebar, reopened
|
|
30
|
+
# above to skip its normal #sidebar rendering when this is on) into
|
|
31
|
+
# the same row as the Batch Actions button/scopes tabs (AA core's own
|
|
32
|
+
# ".table_tools", rendered by Pages::Index#build_table_tools) instead
|
|
33
|
+
# of ActiveAdmin's own #sidebar.
|
|
34
|
+
#
|
|
35
|
+
# Why not #sidebar: ActiveAdminPrism.configuration.collapsible_filters
|
|
36
|
+
# squeezes the *entire* #sidebar column down to a 72px icon gutter
|
|
37
|
+
# while the Filters form is collapsed (scss-src/_panels.scss) — every
|
|
38
|
+
# child of #sidebar shrinks with it, this section included. Its
|
|
39
|
+
# content (one "field contains value" sentence per active filter) has
|
|
40
|
+
# nowhere to fit in 72px and overflows out over the table instead of
|
|
41
|
+
# shrinking cleanly.
|
|
42
|
+
#
|
|
43
|
+
# ActiveAdminPrism::Configuration#active_filters_bar controls this —
|
|
44
|
+
# false restores ActiveAdmin's stock behavior (rendered inside
|
|
45
|
+
# #sidebar, squeezed along with it when collapsible_filters is on).
|
|
46
|
+
class Index
|
|
47
|
+
# AA core's own #build_table_tools (active_admin/views/pages/index.rb)
|
|
48
|
+
# only renders ".table_tools" at all `if any_table_tools?` (batch
|
|
49
|
+
# actions, scopes, or multiple index presenters) — grouping the
|
|
50
|
+
# controls it does render into their own ".table_tools_actions" wrapper
|
|
51
|
+
# (a new element, not AA's) is what lets scss-src/_panels.scss push
|
|
52
|
+
# this bar to the opposite end of the same flex row via
|
|
53
|
+
# `justify-content: space-between` regardless of whether that group
|
|
54
|
+
# is present at all, rather than only ever being able to sit beside
|
|
55
|
+
# it when it happens to exist.
|
|
56
|
+
def build_table_tools
|
|
57
|
+
show_bar = active_filters_bar?
|
|
58
|
+
return unless any_table_tools? || show_bar
|
|
59
|
+
|
|
60
|
+
div class: "table_tools" do
|
|
61
|
+
if any_table_tools?
|
|
62
|
+
div class: "table_tools_actions" do
|
|
63
|
+
build_batch_actions_selector
|
|
64
|
+
build_scopes
|
|
65
|
+
build_index_list
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
build_active_filters_bar if show_bar
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def active_filters_bar?
|
|
73
|
+
return false unless ActiveAdminPrism.configuration.active_filters_bar
|
|
74
|
+
return false unless active_admin_config
|
|
75
|
+
# active_admin_config should always be an ActiveAdmin::Resource here
|
|
76
|
+
# (only a Resource's :index action ever reaches Pages::Index), but
|
|
77
|
+
# #current_filters_enabled? is defined via Filters::ResourceExtension
|
|
78
|
+
# rather than on ActiveAdmin::Config itself — guard rather than
|
|
79
|
+
# assume, the same defensive style as the rest of this gem.
|
|
80
|
+
return false unless active_admin_config.respond_to?(:current_filters_enabled?)
|
|
81
|
+
return false unless active_admin_config.current_filters_enabled?
|
|
82
|
+
|
|
83
|
+
(params[:q] || params[:scope]).present?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def build_active_filters_bar
|
|
87
|
+
div id: "prism_active_filters_bar", class: "prism-active-filters-bar" do
|
|
88
|
+
active_filters_sidebar_content
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -3,17 +3,21 @@
|
|
|
3
3
|
module ActiveAdmin
|
|
4
4
|
module Views
|
|
5
5
|
module Pages
|
|
6
|
-
# Reopens ActiveAdmin::Views::Pages::Base to
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
6
|
+
# Reopens ActiveAdmin::Views::Pages::Base to render flash messages as
|
|
7
|
+
# floating toast cards (icon + message + dismiss button + a
|
|
8
|
+
# countdown progress bar) instead of ActiveAdmin's plain inline
|
|
9
|
+
# banner, and to render the configured auto-dismiss delay as a data
|
|
10
|
+
# attribute prism.js reads at runtime (see
|
|
11
|
+
# ActiveAdminPrism::Configuration#flash_dismissible /
|
|
10
12
|
# #flash_auto_dismiss / #flash_auto_dismiss_seconds).
|
|
11
13
|
#
|
|
12
14
|
# The stock #build_flash_messages just renders
|
|
13
|
-
# `div message, class: "flash flash_#{type}"` — no room for
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
15
|
+
# `div message, class: "flash flash_#{type}"` — no room for an icon,
|
|
16
|
+
# a button, or a progress bar. When flash_dismissible is false, the
|
|
17
|
+
# dismiss button is dropped; the icon and (if flash_auto_dismiss is
|
|
18
|
+
# on) the progress bar still render either way — see
|
|
19
|
+
# scss-src/_base.scss for the toast styling/positioning and the
|
|
20
|
+
# progress bar's CSS animation.
|
|
17
21
|
#
|
|
18
22
|
# Auto-dismiss timing and the click handler for the dismiss button
|
|
19
23
|
# both live in active_admin_prism/prism.js (CSS alone can't
|
|
@@ -22,35 +26,50 @@ module ActiveAdmin
|
|
|
22
26
|
def build_flash_messages
|
|
23
27
|
config = ActiveAdminPrism.configuration
|
|
24
28
|
flashes_data = { "prism-transition-ms": config.flash_transition_ms.to_i }
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
29
|
+
auto_dismiss_ms = config.flash_auto_dismiss_ms
|
|
30
|
+
flashes_data[:"prism-auto-dismiss-ms"] = auto_dismiss_ms if auto_dismiss_ms
|
|
28
31
|
|
|
29
32
|
div class: "flashes", data: flashes_data do
|
|
30
33
|
flash_messages.each do |type, messages|
|
|
31
34
|
[*messages].each do |message|
|
|
32
|
-
|
|
33
|
-
div class: "flash flash_#{type}" do
|
|
34
|
-
span message, class: "prism-flash-message"
|
|
35
|
-
button class: "prism-flash-dismiss", type: "button",
|
|
36
|
-
"aria-label": "Dismiss", data: { "prism-flash-dismiss": true } do
|
|
37
|
-
text_node ActiveAdminPrism::Icons.svg(:x, css_class: "prism-flash-dismiss-icon", size: 14).html_safe
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
else
|
|
41
|
-
div message, class: "flash flash_#{type}"
|
|
42
|
-
end
|
|
35
|
+
build_flash(type, message, config, auto_dismiss_ms)
|
|
43
36
|
end
|
|
44
37
|
end
|
|
45
38
|
end
|
|
46
39
|
end
|
|
47
40
|
|
|
41
|
+
def build_flash(type, message, config, auto_dismiss_ms)
|
|
42
|
+
flash_options = { class: "flash flash_#{type}" }
|
|
43
|
+
flash_options[:style] = "--prism-flash-duration: #{auto_dismiss_ms}ms" if auto_dismiss_ms
|
|
44
|
+
|
|
45
|
+
div flash_options do
|
|
46
|
+
text_node flash_icon(type)
|
|
47
|
+
span message, class: "prism-flash-message"
|
|
48
|
+
if config.flash_dismissible
|
|
49
|
+
button class: "prism-flash-dismiss", type: "button",
|
|
50
|
+
"aria-label": "Dismiss", data: { "prism-flash-dismiss": true } do
|
|
51
|
+
text_node ActiveAdminPrism::Icons.svg(:x, css_class: "prism-flash-dismiss-icon", size: 14).html_safe
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
div class: "prism-flash-progress" if auto_dismiss_ms
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# See lib/prism_icons.rb's ActiveAdminPrism::Icons.flash_icon_name —
|
|
59
|
+
# shared with the plain-ERB logged-out layout, which needs the same
|
|
60
|
+
# mapping but can't reach this Arbre-only method.
|
|
61
|
+
def flash_icon(type)
|
|
62
|
+
name = ActiveAdminPrism::Icons.flash_icon_name(type)
|
|
63
|
+
(ActiveAdminPrism::Icons.svg(name, css_class: "prism-flash-icon") || "").html_safe
|
|
64
|
+
end
|
|
65
|
+
|
|
48
66
|
# Read by prism.js / scss-src's CSS to decide whether to route
|
|
49
67
|
# data-confirm links through the styled dialog, whether the Filters
|
|
50
|
-
# sidebar panel collapses to an icon,
|
|
51
|
-
# original #footer should stay hidden
|
|
68
|
+
# sidebar panel collapses to an icon, whether ActiveAdmin's
|
|
69
|
+
# original #footer should stay hidden, and whether every plain
|
|
70
|
+
# <select> should be auto-enhanced into Select2 (see
|
|
52
71
|
# ActiveAdminPrism::Configuration #styled_confirms /
|
|
53
|
-
# #collapsible_filters / #sidebar_footer).
|
|
72
|
+
# #collapsible_filters / #sidebar_footer / #select2).
|
|
54
73
|
#
|
|
55
74
|
# Can't call `super` here — #body_classes is defined directly on
|
|
56
75
|
# this same class (not a superclass), so reopening and redefining
|
|
@@ -69,6 +88,7 @@ module ActiveAdmin
|
|
|
69
88
|
classes << "prism-styled-confirms-disabled" unless config.styled_confirms
|
|
70
89
|
classes << "prism-filters-collapsible-disabled" unless config.collapsible_filters
|
|
71
90
|
classes << "prism-sidebar-footer-disabled" unless config.sidebar_footer
|
|
91
|
+
classes << "prism-select2-enabled" if config.select2
|
|
72
92
|
classes
|
|
73
93
|
end
|
|
74
94
|
end
|
|
@@ -40,7 +40,10 @@ module ActiveAdmin
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
div class: "prism-sidebar-inner" do
|
|
43
|
-
div(class: "prism-sidebar-brand")
|
|
43
|
+
div(class: "prism-sidebar-brand") do
|
|
44
|
+
render_collapse_toggle if ActiveAdminPrism.configuration.sidebar_collapsible
|
|
45
|
+
site_title @namespace
|
|
46
|
+
end
|
|
44
47
|
render_language_switcher if ActiveAdminPrism.configuration.language_switcher
|
|
45
48
|
render_search_box if ActiveAdminPrism.configuration.menu_search
|
|
46
49
|
div(class: "prism-sidebar-scroll") do
|
|
@@ -79,6 +82,27 @@ module ActiveAdmin
|
|
|
79
82
|
).html_safe
|
|
80
83
|
end
|
|
81
84
|
|
|
85
|
+
# A hamburger icon button at the top of the sidebar, beside the brand
|
|
86
|
+
# — the same :menu icon the mobile off-canvas toggle uses, so both
|
|
87
|
+
# read as "the sidebar toggle" at a glance even though they're two
|
|
88
|
+
# separate buttons for two separate behaviors (this one only ever
|
|
89
|
+
# renders/matters at desktop widths; see scss-src/_sidebar.scss).
|
|
90
|
+
# Toggles the whole sidebar between full width and a narrow
|
|
91
|
+
# icon-only rail — see ActiveAdminPrism::Configuration
|
|
92
|
+
# #sidebar_collapsible and js-src/prism.js for the click handler
|
|
93
|
+
# (persists to localStorage) and scss-src/_sidebar.scss for the
|
|
94
|
+
# collapsed-state styling.
|
|
95
|
+
def render_collapse_toggle
|
|
96
|
+
span(
|
|
97
|
+
class: "prism-sidebar-collapse-toggle",
|
|
98
|
+
data: { "prism-toggle-sidebar-collapse": true },
|
|
99
|
+
role: "button", tabindex: "0", "aria-expanded": "true",
|
|
100
|
+
"aria-label": "Collapse sidebar"
|
|
101
|
+
) do
|
|
102
|
+
text_node ActiveAdminPrism::Icons.svg(:menu, css_class: "prism-sidebar-collapse-icon").html_safe
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
82
106
|
# A small "Languages" dropdown near the top of the sidebar — see
|
|
83
107
|
# ActiveAdminPrism::Configuration#language_switcher/#languages. Built
|
|
84
108
|
# here directly (rather than via the `admin.build_menu
|
|
@@ -191,7 +215,12 @@ module ActiveAdmin
|
|
|
191
215
|
end
|
|
192
216
|
|
|
193
217
|
def render_group_toggle(item, label)
|
|
194
|
-
|
|
218
|
+
# title: a plain native tooltip — inert most of the time (the
|
|
219
|
+
# sidebar already shows this label as text), but the only way to
|
|
220
|
+
# identify this item by hover once ActiveAdminPrism::Configuration
|
|
221
|
+
# #sidebar_collapsible has shrunk the sidebar to its icon-only rail
|
|
222
|
+
# and .prism-nav-label itself is hidden (see scss-src/_sidebar.scss).
|
|
223
|
+
span(class: "prism-nav-group-toggle", data: { "prism-toggle": true }, title: label) do
|
|
195
224
|
text_node icon_for(item)
|
|
196
225
|
span(label, class: "prism-nav-label")
|
|
197
226
|
text_node chevron_svg
|
|
@@ -201,11 +230,14 @@ module ActiveAdmin
|
|
|
201
230
|
def render_leaf(item, label, url)
|
|
202
231
|
link_options = item.html_options.except(:icon)
|
|
203
232
|
link_options[:class] = ["prism-nav-link", link_options[:class]].compact.join(" ")
|
|
233
|
+
# Same rail-collapse tooltip as render_group_toggle above — a host's
|
|
234
|
+
# own explicit :title (via html_options) wins over the label.
|
|
235
|
+
link_options[:title] ||= label
|
|
204
236
|
|
|
205
237
|
if real_url?(url)
|
|
206
238
|
text_node helpers.link_to(link_body(item, label), url, **link_options)
|
|
207
239
|
else
|
|
208
|
-
span(class: "prism-nav-link") { link_body(item, label) }
|
|
240
|
+
span(class: "prism-nav-link", title: label) { link_body(item, label) }
|
|
209
241
|
end
|
|
210
242
|
end
|
|
211
243
|
|
|
@@ -149,6 +149,56 @@ module ActiveAdminPrism
|
|
|
149
149
|
# all.
|
|
150
150
|
attr_accessor :menu_search
|
|
151
151
|
|
|
152
|
+
# Whether every plain <select> ActiveAdmin renders (filters, form
|
|
153
|
+
# inputs, association pickers — anything, no per-field opt-in needed)
|
|
154
|
+
# is automatically enhanced into a searchable Select2 widget. false
|
|
155
|
+
# (the default) leaves every select exactly as ActiveAdmin renders it;
|
|
156
|
+
# you can still opt individual fields into Select2 yourself either way
|
|
157
|
+
# (see INTEGRATION.md's Select2 section) — this flag only controls the
|
|
158
|
+
# blanket, no-setup-needed behavior. Select2 itself (JS + its own base
|
|
159
|
+
# CSS) ships vendored inside this gem's assets either way — turning
|
|
160
|
+
# this on requires no extra gem/npm dependency or JS of your own;
|
|
161
|
+
# see js-src/prism.js and vendor/select2/.
|
|
162
|
+
attr_accessor :select2
|
|
163
|
+
|
|
164
|
+
# Whether the "current scope + active filters" summary (AA core's own
|
|
165
|
+
# "Search status" sidebar section, auto-added to every resource) renders
|
|
166
|
+
# as a compact bar above the index table instead of inside #sidebar.
|
|
167
|
+
# true (the default) avoids it getting squeezed/overflowing alongside
|
|
168
|
+
# the Filters panel when collapsible_filters is on (see
|
|
169
|
+
# lib/active_admin/views/active_filters_bar.rb). false restores
|
|
170
|
+
# ActiveAdmin's stock behavior — rendered inside #sidebar as its own
|
|
171
|
+
# panel, above/below the Filters form depending on registration order.
|
|
172
|
+
attr_accessor :active_filters_bar
|
|
173
|
+
|
|
174
|
+
# Whether a toggle button renders at the bottom of the Prism sidebar
|
|
175
|
+
# (below the nav/account area) that collapses it to a narrow icon-only
|
|
176
|
+
# rail on desktop, expanding back to full width (with labels) on
|
|
177
|
+
# another click — state persists across page loads via localStorage,
|
|
178
|
+
# the same convention already used for per-group nav expand/collapse
|
|
179
|
+
# state (see lib/active_admin/views/prism_sidebar.rb and js-src/prism.js).
|
|
180
|
+
# Purely a desktop feature: the existing off-canvas mobile toggle
|
|
181
|
+
# (below 900px) is unaffected either way. false renders no toggle at
|
|
182
|
+
# all — the sidebar is always full width, matching this gem's behavior
|
|
183
|
+
# before this flag existed.
|
|
184
|
+
attr_accessor :sidebar_collapsible
|
|
185
|
+
|
|
186
|
+
# Milliseconds equivalent of #flash_auto_dismiss_seconds, or nil when
|
|
187
|
+
# #flash_auto_dismiss is off — the exact number both flash-rendering
|
|
188
|
+
# paths this gem ships (lib/active_admin/views/flash_messages.rb's
|
|
189
|
+
# Arbre override for logged-in pages, and the plain-ERB
|
|
190
|
+
# app/views/layouts/active_admin_logged_out.html.erb for Devise's
|
|
191
|
+
# sign-in/password/etc pages) need to render identically: as the
|
|
192
|
+
# "data-prism-auto-dismiss-ms" attribute js-src/prism.js reads to
|
|
193
|
+
# schedule removal, and as each flash's own
|
|
194
|
+
# "--prism-flash-duration" inline style the countdown bar
|
|
195
|
+
# (scss-src/_base.scss's ".prism-flash-progress") animates against.
|
|
196
|
+
def flash_auto_dismiss_ms
|
|
197
|
+
return nil unless flash_auto_dismiss
|
|
198
|
+
|
|
199
|
+
(flash_auto_dismiss_seconds.to_f * 1000).round
|
|
200
|
+
end
|
|
201
|
+
|
|
152
202
|
def initialize
|
|
153
203
|
@sidebar = true
|
|
154
204
|
@colorize_action_icons = true
|
|
@@ -170,6 +220,9 @@ module ActiveAdminPrism
|
|
|
170
220
|
{ label: "Français", locale: :fr }
|
|
171
221
|
]
|
|
172
222
|
@menu_search = true
|
|
223
|
+
@select2 = false
|
|
224
|
+
@active_filters_bar = true
|
|
225
|
+
@sidebar_collapsible = true
|
|
173
226
|
end
|
|
174
227
|
end
|
|
175
228
|
|
data/lib/active_admin_prism.rb
CHANGED
|
@@ -12,6 +12,7 @@ require_relative "active_admin/views/prism_sidebar"
|
|
|
12
12
|
require_relative "active_admin/views/index_table_actions"
|
|
13
13
|
require_relative "active_admin/views/flash_messages"
|
|
14
14
|
require_relative "active_admin/views/filters_sidebar"
|
|
15
|
+
require_relative "active_admin/views/active_filters_bar"
|
|
15
16
|
require_relative "formtastic/inputs/prism_toggle_input"
|
|
16
17
|
|
|
17
18
|
module ActiveAdminPrism
|
data/lib/prism_icons.rb
CHANGED
|
@@ -36,7 +36,11 @@ module ActiveAdminPrism
|
|
|
36
36
|
'<path d="M10 11v6M14 11v6"/>',
|
|
37
37
|
filter: '<path d="M4 5h16l-6.5 7.5v6l-3 1.5v-7.5Z"/>',
|
|
38
38
|
globe: '<circle cx="12" cy="12" r="9"/><path d="M3 12h18"/>' \
|
|
39
|
-
'<path d="M12 3c2.5 2.4 4 5.6 4 9s-1.5 6.6-4 9c-2.5-2.4-4-5.6-4-9s1.5-6.6 4-9Z"/>'
|
|
39
|
+
'<path d="M12 3c2.5 2.4 4 5.6 4 9s-1.5 6.6-4 9c-2.5-2.4-4-5.6-4-9s1.5-6.6 4-9Z"/>',
|
|
40
|
+
check_circle: '<circle cx="12" cy="12" r="9"/><path d="M8 12.5l2.5 2.5L16 9"/>',
|
|
41
|
+
alert_circle: '<circle cx="12" cy="12" r="9"/><path d="M12 7.5v6"/><circle cx="12" cy="16.5" r="0.1" fill="currentColor" stroke-width="2.5"/>',
|
|
42
|
+
alert_triangle: '<path d="M12 4 2.5 20h19L12 4Z"/><path d="M12 10v4.5"/><circle cx="12" cy="17.2" r="0.1" fill="currentColor" stroke-width="2.5"/>',
|
|
43
|
+
info: '<circle cx="12" cy="12" r="9"/><path d="M12 11v5.5"/><circle cx="12" cy="7.8" r="0.1" fill="currentColor" stroke-width="2.5"/>'
|
|
40
44
|
}.freeze
|
|
41
45
|
|
|
42
46
|
# Returns an inline <svg>...</svg> string for `name`, or nil if unknown.
|
|
@@ -49,6 +53,27 @@ module ActiveAdminPrism
|
|
|
49
53
|
%(stroke-linejoin="round" aria-hidden="true">#{inner}</svg>)
|
|
50
54
|
end
|
|
51
55
|
|
|
56
|
+
# Maps a flash's key (Rails' own :notice/:alert conventions, plus
|
|
57
|
+
# :error/:warning/:success/:info as an app might set directly) to one
|
|
58
|
+
# of the four icons above — anything else (a host's own custom flash
|
|
59
|
+
# key) falls back to the neutral :info icon rather than none at all.
|
|
60
|
+
# Shared by both flash-rendering paths this gem ships: the Arbre-based
|
|
61
|
+
# one for logged-in pages (lib/active_admin/views/flash_messages.rb)
|
|
62
|
+
# and the plain-ERB logged-out layout (app/views/layouts/
|
|
63
|
+
# active_admin_logged_out.html.erb) — Devise's sign-in/password/etc
|
|
64
|
+
# pages render through a completely separate layout ActiveAdmin
|
|
65
|
+
# itself ships, not through Pages::Base/Arbre at all, so that
|
|
66
|
+
# Ruby-side override alone never reaches them.
|
|
67
|
+
FLASH_ICONS = {
|
|
68
|
+
notice: :check_circle, success: :check_circle,
|
|
69
|
+
alert: :alert_triangle, warning: :alert_triangle,
|
|
70
|
+
error: :alert_circle
|
|
71
|
+
}.freeze
|
|
72
|
+
|
|
73
|
+
def self.flash_icon_name(type)
|
|
74
|
+
FLASH_ICONS[type.to_sym] || :info
|
|
75
|
+
end
|
|
76
|
+
|
|
52
77
|
# The default sign-in page mark (see ActiveAdminPrism::Configuration
|
|
53
78
|
# #login_logo) — a generic glass-prism-with-spectrum motif distinct from
|
|
54
79
|
# the 24x24 line-icon set above (bigger, multi-color, animated via
|