active_admin_prism 0.1.2 → 0.1.4
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 +159 -0
- data/INTEGRATION.md +274 -35
- data/README.md +5 -1
- data/app/assets/javascripts/active_admin_prism/prism.js +1 -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 +565 -27
- data/lib/active_admin/views/action_items.rb +32 -0
- data/lib/active_admin/views/active_filters_bar.rb +94 -0
- data/lib/active_admin/views/filters_sidebar.rb +42 -13
- data/lib/active_admin/views/flash_messages.rb +40 -22
- data/lib/active_admin/views/prism_sidebar.rb +39 -6
- data/lib/active_admin/views/title_bar.rb +27 -0
- data/lib/active_admin_prism/configuration.rb +80 -0
- data/lib/active_admin_prism/version.rb +1 -1
- data/lib/active_admin_prism.rb +3 -0
- data/lib/prism_icons.rb +47 -1
- data/scss-src/_base.scss +161 -24
- data/scss-src/_buttons.scss +70 -0
- data/scss-src/_forms.scss +15 -0
- data/scss-src/_login.scss +5 -4
- data/scss-src/_panels.scss +189 -14
- data/scss-src/_select2.scss +85 -12
- data/scss-src/_sidebar.scss +145 -4
- data/scss-src/_tables.scss +102 -4
- data/scss-src/_variables.scss +3 -0
- metadata +7 -6
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAdmin
|
|
4
|
+
module Views
|
|
5
|
+
# Reopens ActiveAdmin::Views::ActionItems purely to mark the
|
|
6
|
+
# auto-registered "New <Resource>" action item with its own class —
|
|
7
|
+
# ActiveAdmin::Resource::ActionItems#add_default_new_action_item names
|
|
8
|
+
# it :new internally, but that name never reaches the rendered
|
|
9
|
+
# `span.action_item` itself (ActionItem#html_class only ever emits
|
|
10
|
+
# "action_item" plus a host's own optional :class, nothing derived
|
|
11
|
+
# from the name), so there'd otherwise be no way for CSS/JS to tell it
|
|
12
|
+
# apart from any other action_item at all.
|
|
13
|
+
#
|
|
14
|
+
# js-src/prism.js's consolidated-action-items dropdown reads this to
|
|
15
|
+
# always exclude the New-resource button from consolidation, leaving
|
|
16
|
+
# it inline in its original spot — it's the single most-used action on
|
|
17
|
+
# an index page, unlike the bulk-action/CSV-upload links this feature
|
|
18
|
+
# was actually built to tidy away, so sweeping it into a dropdown too
|
|
19
|
+
# would bury the one button visitors reach for constantly.
|
|
20
|
+
class ActionItems
|
|
21
|
+
def build(action_items)
|
|
22
|
+
action_items.each do |action_item|
|
|
23
|
+
classes = [action_item.html_class]
|
|
24
|
+
classes << "prism-action-item-new" if action_item.name == :new
|
|
25
|
+
span class: classes.join(" ") do
|
|
26
|
+
instance_exec(&action_item.block)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -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,22 +3,36 @@
|
|
|
3
3
|
module ActiveAdmin
|
|
4
4
|
module Views
|
|
5
5
|
# Reopens ActiveAdmin::Views::SidebarSection (used for every sidebar
|
|
6
|
-
# section
|
|
7
|
-
# `sidebar
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
6
|
+
# section — "Filters", "Search Status", and any custom
|
|
7
|
+
# `sidebar "Title" do ... end` block a host app registers) to collapse
|
|
8
|
+
# *every* section to a single icon button by default, expanding to the
|
|
9
|
+
# full panel on click — not just the Filters section, which is all this
|
|
10
|
+
# used to do.
|
|
11
11
|
#
|
|
12
|
-
#
|
|
13
|
-
# while
|
|
14
|
-
# scss-src/_panels.scss's
|
|
15
|
-
#
|
|
12
|
+
# Why generalize this: the #sidebar column's own width only shrinks to
|
|
13
|
+
# a 72px icon gutter while every one of its sections is collapsed (see
|
|
14
|
+
# scss-src/_panels.scss's ":has(.prism-collapsible-panel):not(:has(...open))"
|
|
15
|
+
# rule) — a section left OUT of that treatment still renders at its
|
|
16
|
+
# normal full width/height inside that shrunk 72px column, wrapping
|
|
17
|
+
# every word onto its own line (or, depending on its own CSS, spilling
|
|
18
|
+
# out unclipped over the main content) instead of collapsing cleanly
|
|
19
|
+
# alongside Filters. A host's own custom sidebar block (e.g. a "More
|
|
20
|
+
# Actions" link list) hit exactly this before this file generalized
|
|
21
|
+
# the treatment to cover it too.
|
|
22
|
+
#
|
|
23
|
+
# A host can pass its own icon via `sidebar "Title", icon: :list do
|
|
24
|
+
# ... end` (any name from lib/prism_icons.rb) — anything else, plus
|
|
25
|
+
# every section that predates this option, falls back to a generic
|
|
26
|
+
# icon (:filter for the Filters section specifically, matching its
|
|
27
|
+
# pre-existing look; :list for everything else).
|
|
16
28
|
#
|
|
17
29
|
# This only runs when ActiveAdminPrism.configuration.collapsible_filters
|
|
18
|
-
# is true — when it's false
|
|
19
|
-
# renders it by default (plain text title, no icon
|
|
30
|
+
# is true — when it's false, every section is left exactly as
|
|
31
|
+
# ActiveAdmin renders it by default (plain text title, no icon, never
|
|
32
|
+
# collapsed).
|
|
20
33
|
class SidebarSection < Panel
|
|
21
34
|
FILTERS_SECTION_ID = "filters_sidebar_section"
|
|
35
|
+
DEFAULT_ICON = :list
|
|
22
36
|
|
|
23
37
|
def build(section)
|
|
24
38
|
@section = section
|
|
@@ -26,7 +40,7 @@ module ActiveAdmin
|
|
|
26
40
|
add_class @section.custom_class if @section.custom_class
|
|
27
41
|
self.id = @section.id
|
|
28
42
|
|
|
29
|
-
if
|
|
43
|
+
if ActiveAdminPrism.configuration.collapsible_filters
|
|
30
44
|
add_class "prism-collapsible-panel"
|
|
31
45
|
|
|
32
46
|
# Panel#build already appended the plain-text @title (h3) as the
|
|
@@ -48,7 +62,7 @@ module ActiveAdmin
|
|
|
48
62
|
# be explicitly deleted from wherever it landed before unshifting
|
|
49
63
|
# it to the front — otherwise it ends up in `children` twice.
|
|
50
64
|
children.delete(@title)
|
|
51
|
-
icon = ActiveAdminPrism::Icons.svg(
|
|
65
|
+
icon = ActiveAdminPrism::Icons.svg(icon_name, css_class: "prism-filter-icon")
|
|
52
66
|
contents = @contents
|
|
53
67
|
@contents = nil
|
|
54
68
|
new_title = h3 do
|
|
@@ -64,6 +78,21 @@ module ActiveAdmin
|
|
|
64
78
|
|
|
65
79
|
build_sidebar_content
|
|
66
80
|
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# `options[:icon]` is a new convention this gem adds to the stock
|
|
85
|
+
# `sidebar "Title", only: [...] do ... end` DSL — ActiveAdmin::
|
|
86
|
+
# SidebarSection itself just stores the whole options hash
|
|
87
|
+
# untouched, so a host can already pass `icon:` today without any
|
|
88
|
+
# of their own version needing to change; it's a no-op until this
|
|
89
|
+
# gem starts reading it here.
|
|
90
|
+
def icon_name
|
|
91
|
+
return @section.options[:icon] if @section.options[:icon]
|
|
92
|
+
return :filter if @section.id == FILTERS_SECTION_ID
|
|
93
|
+
|
|
94
|
+
DEFAULT_ICON
|
|
95
|
+
end
|
|
67
96
|
end
|
|
68
97
|
end
|
|
69
98
|
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,29 +26,43 @@ 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
68
|
# sidebar panel collapses to an icon, whether ActiveAdmin's
|
|
@@ -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,8 +215,13 @@ module ActiveAdmin
|
|
|
191
215
|
end
|
|
192
216
|
|
|
193
217
|
def render_group_toggle(item, label)
|
|
194
|
-
|
|
195
|
-
|
|
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
|
|
224
|
+
text_node icon_for(item, label)
|
|
196
225
|
span(label, class: "prism-nav-label")
|
|
197
226
|
text_node chevron_svg
|
|
198
227
|
end
|
|
@@ -201,22 +230,26 @@ 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
|
|
|
212
244
|
def link_body(item, label)
|
|
213
|
-
helpers.safe_join([icon_for(item), helpers.content_tag(:span, label, class: "prism-nav-label")])
|
|
245
|
+
helpers.safe_join([icon_for(item, label), helpers.content_tag(:span, label, class: "prism-nav-label")])
|
|
214
246
|
end
|
|
215
247
|
|
|
216
248
|
# Always returns an html_safe string (possibly empty) — never escape-able
|
|
217
249
|
# raw markup should cross a text_node/content_tag boundary unmarked.
|
|
218
|
-
def icon_for(item)
|
|
250
|
+
def icon_for(item, label)
|
|
219
251
|
name = item.html_options[:icon] || DEFAULT_ITEM_ICONS[item.id]
|
|
252
|
+
name ||= ActiveAdminPrism::Icons.auto_nav_icon_name(label) if ActiveAdminPrism.configuration.auto_nav_icons
|
|
220
253
|
return "".html_safe unless name
|
|
221
254
|
|
|
222
255
|
(ActiveAdminPrism::Icons.svg(name, css_class: "prism-nav-icon") || "").html_safe
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAdmin
|
|
4
|
+
module Views
|
|
5
|
+
# Reopens ActiveAdmin::Views::TitleBar purely to render
|
|
6
|
+
# ActiveAdminPrism::Configuration#action_items_dropdown_threshold as a
|
|
7
|
+
# data attribute on "#titlebar_right" — js-src/prism.js reads it at
|
|
8
|
+
# runtime to decide whether the action_item buttons it finds inside
|
|
9
|
+
# need consolidating into a dropdown (see that file, and
|
|
10
|
+
# scss-src/_buttons.scss for the dropdown's own styling). Nothing else
|
|
11
|
+
# about title bar rendering changes; this can't be done in CSS alone
|
|
12
|
+
# since deciding whether to consolidate depends on *counting* how many
|
|
13
|
+
# action_items actually rendered, not just their presence.
|
|
14
|
+
class TitleBar
|
|
15
|
+
def build_titlebar_right
|
|
16
|
+
config = ActiveAdminPrism.configuration
|
|
17
|
+
data = {}
|
|
18
|
+
data[:"prism-action-items-threshold"] = config.action_items_dropdown_threshold if config.action_items_dropdown
|
|
19
|
+
|
|
20
|
+
div id: "titlebar_right", data: data do
|
|
21
|
+
build_action_items
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
private :build_titlebar_right
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -161,6 +161,81 @@ module ActiveAdminPrism
|
|
|
161
161
|
# see js-src/prism.js and vendor/select2/.
|
|
162
162
|
attr_accessor :select2
|
|
163
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
|
+
# Whether a title bar with more than #action_items_dropdown_threshold
|
|
187
|
+
# `action_item` buttons collapses them into a single "Actions"
|
|
188
|
+
# dropdown instead of rendering every one inline. A resource that
|
|
189
|
+
# registers many (a dozen+ CSV upload / bulk-action links is a real
|
|
190
|
+
# example this was built for) turns the title bar into a multi-row
|
|
191
|
+
# wall of buttons otherwise — see js-src/prism.js's action-items
|
|
192
|
+
# handler, which does the actual consolidation client-side, and
|
|
193
|
+
# lib/active_admin/views/title_bar.rb for the threshold this reads.
|
|
194
|
+
# false never consolidates — every action_item always renders inline,
|
|
195
|
+
# matching this gem's behavior before this flag existed.
|
|
196
|
+
attr_accessor :action_items_dropdown
|
|
197
|
+
|
|
198
|
+
# How many `action_item`s (not counting the auto-added "New Resource"
|
|
199
|
+
# button, which is never counted or consolidated — see
|
|
200
|
+
# lib/active_admin/views/action_items.rb) a title bar can have before
|
|
201
|
+
# #action_items_dropdown (if true) collapses them into a dropdown.
|
|
202
|
+
# Default 0 means the "Actions" Select2 dropdown is what a host sees
|
|
203
|
+
# by default the moment they add even a single custom action_item,
|
|
204
|
+
# rather than only once they've piled up "many" of them — raise this
|
|
205
|
+
# if you'd rather a page with just one or two extra actions kept
|
|
206
|
+
# rendering them inline instead.
|
|
207
|
+
attr_accessor :action_items_dropdown_threshold
|
|
208
|
+
|
|
209
|
+
# Whether a Pages nav item with no explicit `icon:` (and not one of
|
|
210
|
+
# the handful this gem itself always icons — the current-user/logout
|
|
211
|
+
# rows) gets one anyway instead of rendering label-less, or as just a
|
|
212
|
+
# bare dot once the sidebar is collapsed to its icon-only rail (see
|
|
213
|
+
# ActiveAdminPrism::Configuration#sidebar_collapsible). Deterministic
|
|
214
|
+
# per item, not re-rolled on every request — the same menu label
|
|
215
|
+
# always picks the same icon from lib/prism_icons.rb's
|
|
216
|
+
# ActiveAdminPrism::Icons::NAV_ICON_POOL (see
|
|
217
|
+
# lib/active_admin/views/prism_sidebar.rb#icon_for), so it doesn't
|
|
218
|
+
# visibly shuffle around between page loads. false renders no icon at
|
|
219
|
+
# all for such an item, matching this gem's behavior before this flag
|
|
220
|
+
# existed.
|
|
221
|
+
attr_accessor :auto_nav_icons
|
|
222
|
+
|
|
223
|
+
# Milliseconds equivalent of #flash_auto_dismiss_seconds, or nil when
|
|
224
|
+
# #flash_auto_dismiss is off — the exact number both flash-rendering
|
|
225
|
+
# paths this gem ships (lib/active_admin/views/flash_messages.rb's
|
|
226
|
+
# Arbre override for logged-in pages, and the plain-ERB
|
|
227
|
+
# app/views/layouts/active_admin_logged_out.html.erb for Devise's
|
|
228
|
+
# sign-in/password/etc pages) need to render identically: as the
|
|
229
|
+
# "data-prism-auto-dismiss-ms" attribute js-src/prism.js reads to
|
|
230
|
+
# schedule removal, and as each flash's own
|
|
231
|
+
# "--prism-flash-duration" inline style the countdown bar
|
|
232
|
+
# (scss-src/_base.scss's ".prism-flash-progress") animates against.
|
|
233
|
+
def flash_auto_dismiss_ms
|
|
234
|
+
return nil unless flash_auto_dismiss
|
|
235
|
+
|
|
236
|
+
(flash_auto_dismiss_seconds.to_f * 1000).round
|
|
237
|
+
end
|
|
238
|
+
|
|
164
239
|
def initialize
|
|
165
240
|
@sidebar = true
|
|
166
241
|
@colorize_action_icons = true
|
|
@@ -183,6 +258,11 @@ module ActiveAdminPrism
|
|
|
183
258
|
]
|
|
184
259
|
@menu_search = true
|
|
185
260
|
@select2 = false
|
|
261
|
+
@active_filters_bar = true
|
|
262
|
+
@sidebar_collapsible = true
|
|
263
|
+
@action_items_dropdown = true
|
|
264
|
+
@action_items_dropdown_threshold = 0
|
|
265
|
+
@auto_nav_icons = true
|
|
186
266
|
end
|
|
187
267
|
end
|
|
188
268
|
|
data/lib/active_admin_prism.rb
CHANGED
|
@@ -12,6 +12,9 @@ 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"
|
|
16
|
+
require_relative "active_admin/views/title_bar"
|
|
17
|
+
require_relative "active_admin/views/action_items"
|
|
15
18
|
require_relative "formtastic/inputs/prism_toggle_input"
|
|
16
19
|
|
|
17
20
|
module ActiveAdminPrism
|
data/lib/prism_icons.rb
CHANGED
|
@@ -36,9 +36,34 @@ 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
|
|
|
46
|
+
# Candidates for ActiveAdminPrism::Configuration#auto_nav_icons — a
|
|
47
|
+
# generic-enough subset of ICONS above that any of them reads as
|
|
48
|
+
# plausible next to an arbitrary menu label (excludes chevron_down,
|
|
49
|
+
# check/x, the alert/info trio and logout: purpose-specific shapes
|
|
50
|
+
# that would look wrong or actively misleading picked at random for,
|
|
51
|
+
# say, a "Reports" link).
|
|
52
|
+
NAV_ICON_POOL = %i[
|
|
53
|
+
dashboard users cart box receipt credit_card message settings
|
|
54
|
+
home list folder bell search globe eye pencil filter
|
|
55
|
+
].freeze
|
|
56
|
+
|
|
57
|
+
# Deterministic, not actually random — the same `label` always maps
|
|
58
|
+
# to the same icon (a simple sum-of-bytes hash, stable across
|
|
59
|
+
# processes/restarts unlike Ruby's own salted String#hash), so a
|
|
60
|
+
# nav item's auto-picked icon doesn't visibly shuffle between page
|
|
61
|
+
# loads. See lib/active_admin/views/prism_sidebar.rb#icon_for, the
|
|
62
|
+
# only caller.
|
|
63
|
+
def self.auto_nav_icon_name(label)
|
|
64
|
+
NAV_ICON_POOL[label.to_s.each_byte.sum % NAV_ICON_POOL.length]
|
|
65
|
+
end
|
|
66
|
+
|
|
42
67
|
# Returns an inline <svg>...</svg> string for `name`, or nil if unknown.
|
|
43
68
|
def self.svg(name, css_class: "prism-icon", size: 18)
|
|
44
69
|
inner = ICONS[name.to_sym]
|
|
@@ -49,6 +74,27 @@ module ActiveAdminPrism
|
|
|
49
74
|
%(stroke-linejoin="round" aria-hidden="true">#{inner}</svg>)
|
|
50
75
|
end
|
|
51
76
|
|
|
77
|
+
# Maps a flash's key (Rails' own :notice/:alert conventions, plus
|
|
78
|
+
# :error/:warning/:success/:info as an app might set directly) to one
|
|
79
|
+
# of the four icons above — anything else (a host's own custom flash
|
|
80
|
+
# key) falls back to the neutral :info icon rather than none at all.
|
|
81
|
+
# Shared by both flash-rendering paths this gem ships: the Arbre-based
|
|
82
|
+
# one for logged-in pages (lib/active_admin/views/flash_messages.rb)
|
|
83
|
+
# and the plain-ERB logged-out layout (app/views/layouts/
|
|
84
|
+
# active_admin_logged_out.html.erb) — Devise's sign-in/password/etc
|
|
85
|
+
# pages render through a completely separate layout ActiveAdmin
|
|
86
|
+
# itself ships, not through Pages::Base/Arbre at all, so that
|
|
87
|
+
# Ruby-side override alone never reaches them.
|
|
88
|
+
FLASH_ICONS = {
|
|
89
|
+
notice: :check_circle, success: :check_circle,
|
|
90
|
+
alert: :alert_triangle, warning: :alert_triangle,
|
|
91
|
+
error: :alert_circle
|
|
92
|
+
}.freeze
|
|
93
|
+
|
|
94
|
+
def self.flash_icon_name(type)
|
|
95
|
+
FLASH_ICONS[type.to_sym] || :info
|
|
96
|
+
end
|
|
97
|
+
|
|
52
98
|
# The default sign-in page mark (see ActiveAdminPrism::Configuration
|
|
53
99
|
# #login_logo) — a generic glass-prism-with-spectrum motif distinct from
|
|
54
100
|
# the 24x24 line-icon set above (bigger, multi-color, animated via
|