active_admin_prism 0.1.0
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 +7 -0
- data/CHANGELOG.md +133 -0
- data/INTEGRATION.md +1012 -0
- data/LICENSE.txt +21 -0
- data/README.md +319 -0
- data/app/assets/javascripts/active_admin_prism/prism.js +1 -0
- data/app/assets/stylesheets/active_admin_prism/_variable_overrides.scss +43 -0
- data/app/assets/stylesheets/active_admin_prism/prism.css +1 -0
- data/app/views/active_admin/devise/confirmations/new.html.erb +26 -0
- data/app/views/active_admin/devise/passwords/edit.html.erb +29 -0
- data/app/views/active_admin/devise/passwords/new.html.erb +26 -0
- data/app/views/active_admin/devise/registrations/new.html.erb +32 -0
- data/app/views/active_admin/devise/sessions/new.html.erb +37 -0
- data/app/views/active_admin/devise/shared/_brand.html.erb +34 -0
- data/app/views/active_admin/devise/unlocks/new.html.erb +25 -0
- data/js-src/prism.js +270 -0
- data/lib/active_admin/views/filters_sidebar.rb +69 -0
- data/lib/active_admin/views/flash_messages.rb +77 -0
- data/lib/active_admin/views/index_table_actions.rb +46 -0
- data/lib/active_admin/views/prism_sidebar.rb +212 -0
- data/lib/active_admin_prism/configuration.rb +180 -0
- data/lib/active_admin_prism/engine.rb +18 -0
- data/lib/active_admin_prism/version.rb +5 -0
- data/lib/active_admin_prism.rb +33 -0
- data/lib/formtastic/inputs/prism_toggle_input.rb +31 -0
- data/lib/generators/active_admin_prism/error_pages/error_pages_generator.rb +49 -0
- data/lib/generators/active_admin_prism/error_pages/templates/404.html +164 -0
- data/lib/generators/active_admin_prism/error_pages/templates/500.html +173 -0
- data/lib/generators/active_admin_prism/install/install_generator.rb +69 -0
- data/lib/prism_icons.rb +82 -0
- data/lib/prism_login_helper.rb +31 -0
- data/lib/prism_toggle_tag.rb +35 -0
- data/scss-src/_base.scss +281 -0
- data/scss-src/_buttons.scss +134 -0
- data/scss-src/_forms.scss +272 -0
- data/scss-src/_icons.scss +27 -0
- data/scss-src/_login.scss +306 -0
- data/scss-src/_panels.scss +249 -0
- data/scss-src/_sidebar.scss +479 -0
- data/scss-src/_tables.scss +142 -0
- data/scss-src/_variables.scss +101 -0
- data/scss-src/prism.scss +15 -0
- metadata +157 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Shared brand block for every Devise auth page Prism reskins (sign in,
|
|
3
|
+
sign up, forgot/reset password, resend confirmation, resend unlock) —
|
|
4
|
+
each action's own view renders this instead of duplicating the
|
|
5
|
+
logo/app-name/subtitle markup, so all of them redesign (or degrade back
|
|
6
|
+
to stock ActiveAdmin) together instead of drifting out of sync. Gated by
|
|
7
|
+
ActiveAdminPrism::Configuration#login_page, the same flag the
|
|
8
|
+
sign-in page itself uses (see lib/prism_login_helper.rb for the
|
|
9
|
+
logo/app-name resolution logic behind prism_login_logo_html/
|
|
10
|
+
prism_login_app_name).
|
|
11
|
+
|
|
12
|
+
Locals:
|
|
13
|
+
action_title - the page's own translated title (e.g. "Sign up",
|
|
14
|
+
"Change your password") — the caller has already run
|
|
15
|
+
it through `title(...)` for its @page_title side
|
|
16
|
+
effect before passing it in here, so this partial
|
|
17
|
+
never needs to call `title` itself.
|
|
18
|
+
subtitle - what to show under the app name in branded mode.
|
|
19
|
+
Every caller except sessions#new just passes
|
|
20
|
+
action_title again; sessions#new passes
|
|
21
|
+
config.login_tagline instead, since that one has its
|
|
22
|
+
own dedicated, independently configurable copy.
|
|
23
|
+
%>
|
|
24
|
+
<% if ActiveAdminPrism.configuration.login_page %>
|
|
25
|
+
<div class="prism-login-brand">
|
|
26
|
+
<div class="prism-login-logo"><%= prism_login_logo_html %></div>
|
|
27
|
+
<h2 class="prism-login-title"><%= prism_login_app_name %></h2>
|
|
28
|
+
<% if subtitle.present? %>
|
|
29
|
+
<p class="prism-login-tagline"><%= subtitle %></p>
|
|
30
|
+
<% end %>
|
|
31
|
+
</div>
|
|
32
|
+
<% else %>
|
|
33
|
+
<h2><%= active_admin_application.site_title(self) %> <%= action_title %></h2>
|
|
34
|
+
<% end %>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Overrides ActiveAdmin's own active_admin/devise/unlocks/new.html.erb the
|
|
3
|
+
same way sessions/new.html.erb does (see that file's header comment for
|
|
4
|
+
the Gemfile-order mechanism this relies on). Form fields/behavior below
|
|
5
|
+
are byte-for-byte ActiveAdmin's own template; only the header markup
|
|
6
|
+
(shared devise/shared/_brand partial) and the links wrapper are new.
|
|
7
|
+
%>
|
|
8
|
+
<div id="login" class="prism-login">
|
|
9
|
+
<% action_title = title t('active_admin.devise.unlock.title') %>
|
|
10
|
+
<%= render "active_admin/devise/shared/brand", action_title: action_title, subtitle: action_title %>
|
|
11
|
+
|
|
12
|
+
<%= render partial: "active_admin/devise/shared/error_messages", resource: resource %>
|
|
13
|
+
<%= active_admin_form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f|
|
|
14
|
+
f.inputs do
|
|
15
|
+
f.input :email
|
|
16
|
+
end
|
|
17
|
+
f.actions do
|
|
18
|
+
f.action :submit, label: t('active_admin.devise.unlock.submit'), button_html: { value: t('active_admin.devise.unlock.submit') }
|
|
19
|
+
end
|
|
20
|
+
end %>
|
|
21
|
+
|
|
22
|
+
<div class="prism-login-links">
|
|
23
|
+
<%= render partial: "active_admin/devise/shared/links" %>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
data/js-src/prism.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
// This is the real source — mirrors scss-src/*.scss's role for the CSS
|
|
2
|
+
// side. `bin/build-js` minifies this into
|
|
3
|
+
// app/assets/javascripts/active_admin_prism/prism.js, the file actually
|
|
4
|
+
// registered via ActiveAdmin.application.register_javascript and shipped
|
|
5
|
+
// to hosts. Never hand-edit that compiled copy — edit this file and
|
|
6
|
+
// rebuild. See bin/build-css's own comment for why shipping pre-minified
|
|
7
|
+
// assets matters even though Sprockets hosts would otherwise re-minify on
|
|
8
|
+
// their own `assets:precompile` — Propshaft hosts have no such step.
|
|
9
|
+
//
|
|
10
|
+
// Prism sidebar behaviour: collapsible nav groups + mobile off-canvas
|
|
11
|
+
// toggle. Vanilla JS, no dependency on jQuery/Stimulus/a bundler.
|
|
12
|
+
(function () {
|
|
13
|
+
"use strict";
|
|
14
|
+
|
|
15
|
+
var STORAGE_PREFIX = "prism_sidebar_";
|
|
16
|
+
|
|
17
|
+
function storageKey(li) {
|
|
18
|
+
return STORAGE_PREFIX + li.id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function setOpen(li, open) {
|
|
22
|
+
li.classList.toggle("open", open);
|
|
23
|
+
try {
|
|
24
|
+
window.localStorage.setItem(storageKey(li), open ? "1" : "0");
|
|
25
|
+
} catch (e) {
|
|
26
|
+
// localStorage unavailable (private mode, disabled, etc.) — degrade to
|
|
27
|
+
// in-memory only, no persistence across reloads.
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function restoreGroupState(sidebar) {
|
|
32
|
+
var groups = sidebar.querySelectorAll(".prism-nav-item.has-children");
|
|
33
|
+
groups.forEach(function (li) {
|
|
34
|
+
var stored;
|
|
35
|
+
try {
|
|
36
|
+
stored = window.localStorage.getItem(storageKey(li));
|
|
37
|
+
} catch (e) {
|
|
38
|
+
stored = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (stored === "1") {
|
|
42
|
+
li.classList.add("open");
|
|
43
|
+
} else if (stored === "0") {
|
|
44
|
+
li.classList.remove("open");
|
|
45
|
+
}
|
|
46
|
+
// stored === null: leave the server-rendered default (open only when
|
|
47
|
+
// it contains the active item) untouched.
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function onToggleClick(event) {
|
|
52
|
+
var toggle = event.target.closest("[data-prism-toggle]");
|
|
53
|
+
if (!toggle) return;
|
|
54
|
+
|
|
55
|
+
var li = toggle.closest(".prism-nav-item");
|
|
56
|
+
if (!li) return;
|
|
57
|
+
|
|
58
|
+
setOpen(li, !li.classList.contains("open"));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function onSidebarButtonClick(event) {
|
|
62
|
+
var button = event.target.closest("[data-prism-toggle-sidebar]");
|
|
63
|
+
if (!button) return;
|
|
64
|
+
|
|
65
|
+
document.body.classList.toggle("prism-sidebar-open");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
69
|
+
var sidebar = document.getElementById("header");
|
|
70
|
+
if (!sidebar || !sidebar.classList.contains("prism-sidebar")) return;
|
|
71
|
+
|
|
72
|
+
restoreGroupState(sidebar);
|
|
73
|
+
sidebar.addEventListener("click", onToggleClick);
|
|
74
|
+
sidebar.addEventListener("click", onSidebarButtonClick);
|
|
75
|
+
});
|
|
76
|
+
})();
|
|
77
|
+
|
|
78
|
+
// Routes every plain `data-confirm` link (row-level View/Edit/Delete
|
|
79
|
+
// actions, any other rails-ujs confirm) through ActiveAdmin's own styled
|
|
80
|
+
// jQuery UI dialog — the same one it already uses for Batch Actions —
|
|
81
|
+
// instead of the native, unstylable browser confirm(). Batch Actions links
|
|
82
|
+
// are left alone; they already run their own confirm flow (with dynamic
|
|
83
|
+
// per-action inputs) via active_admin/base.js.
|
|
84
|
+
//
|
|
85
|
+
// This overrides $.rails.allowAction, jquery-ujs's documented extension
|
|
86
|
+
// point for custom confirm dialogs (jquery_ujs.js: "may be overridden with
|
|
87
|
+
// custom confirm dialog in $.rails.confirm" / allowAction). allowAction is
|
|
88
|
+
// called synchronously and expects true/false; since our dialog is async,
|
|
89
|
+
// we return false to block the original action, then re-`click()` the same
|
|
90
|
+
// element after the user confirms — allowAction runs again, sees the
|
|
91
|
+
// "prismConfirmed" flag, and returns true so rails-ujs's normal
|
|
92
|
+
// data-method handling proceeds untouched.
|
|
93
|
+
(function () {
|
|
94
|
+
"use strict";
|
|
95
|
+
|
|
96
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
97
|
+
// Set by Pages::Base#body_classes when
|
|
98
|
+
// ActiveAdminPrism.configuration.styled_confirms is false.
|
|
99
|
+
if (document.body.classList.contains("prism-styled-confirms-disabled")) return;
|
|
100
|
+
if (typeof jQuery === "undefined" || !jQuery.rails || !window.ActiveAdmin || !window.ActiveAdmin.ModalDialog) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
var $ = jQuery;
|
|
105
|
+
var originalAllowAction = $.rails.allowAction;
|
|
106
|
+
|
|
107
|
+
$.rails.allowAction = function (element) {
|
|
108
|
+
var message = element.data("confirm");
|
|
109
|
+
if (!message) return originalAllowAction.call($.rails, element);
|
|
110
|
+
if (element.closest(".batch_actions_selector").length) {
|
|
111
|
+
return originalAllowAction.call($.rails, element);
|
|
112
|
+
}
|
|
113
|
+
if (element.data("prismConfirmed")) {
|
|
114
|
+
element.removeData("prismConfirmed");
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
new window.ActiveAdmin.ModalDialog(message, {}, function () {
|
|
119
|
+
element.data("prismConfirmed", true);
|
|
120
|
+
element[0].click();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return false;
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
})();
|
|
127
|
+
|
|
128
|
+
// Flash messages: dismiss button + auto-hide. See
|
|
129
|
+
// lib/active_admin/views/flash_messages.rb, which renders the
|
|
130
|
+
// ".prism-flash-dismiss" button markup this responds to, and two data
|
|
131
|
+
// attributes this reads at runtime so Ruby-side config controls behavior
|
|
132
|
+
// without needing to rebuild this static asset:
|
|
133
|
+
// - "data-prism-auto-dismiss-ms" (present only when
|
|
134
|
+
// ActiveAdminPrism.configuration.flash_auto_dismiss is true)
|
|
135
|
+
// - "data-prism-transition-ms" (ActiveAdminPrism::Configuration
|
|
136
|
+
// #flash_transition_ms) — applied as an inline transition-duration
|
|
137
|
+
// (overriding the CSS default) and reused as the removal delay, so the
|
|
138
|
+
// fade-out animation and the element's actual removal always stay in
|
|
139
|
+
// sync regardless of the configured duration.
|
|
140
|
+
(function () {
|
|
141
|
+
"use strict";
|
|
142
|
+
|
|
143
|
+
var DEFAULT_TRANSITION_MS = 320;
|
|
144
|
+
|
|
145
|
+
function dismiss(flash, transitionMs) {
|
|
146
|
+
if (flash.dataset.prismDismissed) return;
|
|
147
|
+
flash.dataset.prismDismissed = "true";
|
|
148
|
+
flash.style.transitionDuration = transitionMs + "ms";
|
|
149
|
+
flash.classList.add("prism-flash-hide");
|
|
150
|
+
setTimeout(function () {
|
|
151
|
+
flash.remove();
|
|
152
|
+
}, transitionMs);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
156
|
+
var flashesWrapper = document.querySelector(".flashes");
|
|
157
|
+
var flashes = document.querySelectorAll(".flashes .flash");
|
|
158
|
+
if (!flashes.length) return;
|
|
159
|
+
|
|
160
|
+
var transitionMs = (flashesWrapper && parseInt(flashesWrapper.dataset.prismTransitionMs, 10)) || DEFAULT_TRANSITION_MS;
|
|
161
|
+
var autoDismissMs = flashesWrapper && parseInt(flashesWrapper.dataset.prismAutoDismissMs, 10);
|
|
162
|
+
|
|
163
|
+
if (autoDismissMs) {
|
|
164
|
+
flashes.forEach(function (flash) {
|
|
165
|
+
setTimeout(function () {
|
|
166
|
+
dismiss(flash, transitionMs);
|
|
167
|
+
}, autoDismissMs);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
document.addEventListener("click", function (event) {
|
|
172
|
+
var button = event.target.closest("[data-prism-flash-dismiss]");
|
|
173
|
+
if (!button) return;
|
|
174
|
+
dismiss(button.closest(".flash"), transitionMs);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
})();
|
|
178
|
+
|
|
179
|
+
// "Filters" sidebar panel: collapses to a single icon button, expanding to
|
|
180
|
+
// the full form on click. See scss-src/_panels.scss (#filters_sidebar_section)
|
|
181
|
+
// for the actual collapse/expand styling — this only toggles the ".open"
|
|
182
|
+
// class an inline click responds to. Skipped entirely when
|
|
183
|
+
// ActiveAdminPrism.configuration.collapsible_filters is false (the
|
|
184
|
+
// "prism-filters-collapsible-disabled" body class, set in
|
|
185
|
+
// lib/active_admin/views/flash_messages.rb#body_classes).
|
|
186
|
+
(function () {
|
|
187
|
+
"use strict";
|
|
188
|
+
|
|
189
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
190
|
+
if (document.body.classList.contains("prism-filters-collapsible-disabled")) return;
|
|
191
|
+
|
|
192
|
+
var section = document.getElementById("filters_sidebar_section");
|
|
193
|
+
if (!section) return;
|
|
194
|
+
|
|
195
|
+
var toggle = section.querySelector(":scope > h3");
|
|
196
|
+
if (!toggle) return;
|
|
197
|
+
|
|
198
|
+
toggle.setAttribute("role", "button");
|
|
199
|
+
toggle.setAttribute("tabindex", "0");
|
|
200
|
+
toggle.setAttribute("aria-expanded", section.classList.contains("open") ? "true" : "false");
|
|
201
|
+
// Mirrored onto <body> because #main_content (the table/grid area)
|
|
202
|
+
// is a *sibling* of #sidebar, not a descendant of this section — CSS
|
|
203
|
+
// can't reach across siblings, so scss-src/_panels.scss keys the
|
|
204
|
+
// #main_content/#sidebar width reflow off this body class instead.
|
|
205
|
+
document.body.classList.toggle("prism-filters-open", section.classList.contains("open"));
|
|
206
|
+
|
|
207
|
+
function toggleOpen() {
|
|
208
|
+
var open = section.classList.toggle("open");
|
|
209
|
+
toggle.setAttribute("aria-expanded", open ? "true" : "false");
|
|
210
|
+
document.body.classList.toggle("prism-filters-open", open);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
toggle.addEventListener("click", toggleOpen);
|
|
214
|
+
toggle.addEventListener("keydown", function (event) {
|
|
215
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
216
|
+
event.preventDefault();
|
|
217
|
+
toggleOpen();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
})();
|
|
221
|
+
|
|
222
|
+
// "Languages" sidebar dropdown (see
|
|
223
|
+
// lib/active_admin/views/prism_sidebar.rb#render_language_switcher) —
|
|
224
|
+
// click/keyboard toggle plus click-outside-to-close, matching how a normal
|
|
225
|
+
// dropdown popover behaves. Absent from the page entirely (nothing for this
|
|
226
|
+
// to attach to) when ActiveAdminPrism.configuration.language_switcher or
|
|
227
|
+
// #languages is empty, so no explicit disabled-body-class check is needed
|
|
228
|
+
// here, unlike prism.js's other config-gated behaviors.
|
|
229
|
+
(function () {
|
|
230
|
+
"use strict";
|
|
231
|
+
|
|
232
|
+
function closeAll(except) {
|
|
233
|
+
document.querySelectorAll(".prism-sidebar-lang.open").forEach(function (wrapper) {
|
|
234
|
+
if (wrapper === except) return;
|
|
235
|
+
wrapper.classList.remove("open");
|
|
236
|
+
var toggle = wrapper.querySelector(".prism-lang-toggle");
|
|
237
|
+
if (toggle) toggle.setAttribute("aria-expanded", "false");
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
242
|
+
var toggle = document.querySelector(".prism-lang-toggle");
|
|
243
|
+
if (!toggle) return;
|
|
244
|
+
|
|
245
|
+
var wrapper = toggle.closest(".prism-sidebar-lang");
|
|
246
|
+
|
|
247
|
+
function toggleOpen() {
|
|
248
|
+
var open = wrapper.classList.toggle("open");
|
|
249
|
+
toggle.setAttribute("aria-expanded", open ? "true" : "false");
|
|
250
|
+
if (open) closeAll(wrapper);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
toggle.setAttribute("role", "button");
|
|
254
|
+
toggle.setAttribute("tabindex", "0");
|
|
255
|
+
|
|
256
|
+
toggle.addEventListener("click", function (event) {
|
|
257
|
+
event.stopPropagation();
|
|
258
|
+
toggleOpen();
|
|
259
|
+
});
|
|
260
|
+
toggle.addEventListener("keydown", function (event) {
|
|
261
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
262
|
+
event.preventDefault();
|
|
263
|
+
toggleOpen();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
document.addEventListener("click", function () {
|
|
267
|
+
closeAll();
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
})();
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAdmin
|
|
4
|
+
module Views
|
|
5
|
+
# Reopens ActiveAdmin::Views::SidebarSection (used for every sidebar
|
|
6
|
+
# section, e.g. "Filters", "Search Status", any custom
|
|
7
|
+
# `sidebar :title do ... end` block) to inject a real inline SVG icon
|
|
8
|
+
# into the title of the *Filters* section specifically — matching the
|
|
9
|
+
# id ActiveAdmin::SidebarSection#id computes for it
|
|
10
|
+
# ("filters_sidebar_section"). Every other sidebar section is untouched.
|
|
11
|
+
#
|
|
12
|
+
# The label text is wrapped in its own span so CSS can visually hide it
|
|
13
|
+
# while collapsed without touching the icon (see
|
|
14
|
+
# scss-src/_panels.scss's #filters_sidebar_section rules, and
|
|
15
|
+
# prism.js's filters-collapse handler for the actual toggle).
|
|
16
|
+
#
|
|
17
|
+
# This only runs when ActiveAdminPrism.configuration.collapsible_filters
|
|
18
|
+
# is true — when it's false the section is left exactly as ActiveAdmin
|
|
19
|
+
# renders it by default (plain text title, no icon).
|
|
20
|
+
class SidebarSection < Panel
|
|
21
|
+
FILTERS_SECTION_ID = "filters_sidebar_section"
|
|
22
|
+
|
|
23
|
+
def build(section)
|
|
24
|
+
@section = section
|
|
25
|
+
super(@section.title)
|
|
26
|
+
add_class @section.custom_class if @section.custom_class
|
|
27
|
+
self.id = @section.id
|
|
28
|
+
|
|
29
|
+
if @section.id == FILTERS_SECTION_ID && ActiveAdminPrism.configuration.collapsible_filters
|
|
30
|
+
add_class "prism-collapsible-panel"
|
|
31
|
+
|
|
32
|
+
# Panel#build already appended the plain-text @title (h3) as the
|
|
33
|
+
# first child, followed by @contents (the panel_contents div).
|
|
34
|
+
# Replace it with an [icon, label-span] version — built fresh
|
|
35
|
+
# rather than mutated in place (Arbre elements don't expose an
|
|
36
|
+
# API to insert content before an existing child).
|
|
37
|
+
#
|
|
38
|
+
# Panel#add_child redirects *any* add_child call on this object to
|
|
39
|
+
# @contents once @contents exists (so plain builder calls inside a
|
|
40
|
+
# panel's do-block land in .panel_contents, not as top-level
|
|
41
|
+
# siblings) — building a fresh h3 via the normal `h3(...)` verb at
|
|
42
|
+
# this point would silently land *inside* .panel_contents instead
|
|
43
|
+
# of replacing @title. Building it while @contents is
|
|
44
|
+
# (temporarily) nil bypasses that redirect — but add_child still
|
|
45
|
+
# *appends* (to the end of `children`, after the contents div,
|
|
46
|
+
# since that div itself is still sitting in `children` even
|
|
47
|
+
# though the @contents variable is momentarily nil), so it has to
|
|
48
|
+
# be explicitly deleted from wherever it landed before unshifting
|
|
49
|
+
# it to the front — otherwise it ends up in `children` twice.
|
|
50
|
+
children.delete(@title)
|
|
51
|
+
icon = ActiveAdminPrism::Icons.svg(:filter, css_class: "prism-filter-icon")
|
|
52
|
+
contents = @contents
|
|
53
|
+
@contents = nil
|
|
54
|
+
new_title = h3 do
|
|
55
|
+
text_node icon.html_safe if icon
|
|
56
|
+
span @section.title.to_s, class: "prism-filter-label"
|
|
57
|
+
end
|
|
58
|
+
@contents = contents
|
|
59
|
+
|
|
60
|
+
children.delete(new_title)
|
|
61
|
+
children.unshift(new_title)
|
|
62
|
+
@title = new_title
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
build_sidebar_content
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAdmin
|
|
4
|
+
module Views
|
|
5
|
+
module Pages
|
|
6
|
+
# Reopens ActiveAdmin::Views::Pages::Base to add a dismiss ("x")
|
|
7
|
+
# button to each flash message, and to render the configured
|
|
8
|
+
# auto-dismiss delay as a data attribute prism.js reads at runtime
|
|
9
|
+
# (see ActiveAdminPrism::Configuration#flash_dismissible /
|
|
10
|
+
# #flash_auto_dismiss / #flash_auto_dismiss_seconds).
|
|
11
|
+
#
|
|
12
|
+
# The stock #build_flash_messages just renders
|
|
13
|
+
# `div message, class: "flash flash_#{type}"` — no room for a button.
|
|
14
|
+
# When flash_dismissible is false, that exact stock markup is what
|
|
15
|
+
# this renders; only when it's true (the default) does the message
|
|
16
|
+
# get wrapped with a dismiss button alongside it.
|
|
17
|
+
#
|
|
18
|
+
# Auto-dismiss timing and the click handler for the dismiss button
|
|
19
|
+
# both live in active_admin_prism/prism.js (CSS alone can't
|
|
20
|
+
# remove an element from the DOM or run a timer).
|
|
21
|
+
class Base
|
|
22
|
+
def build_flash_messages
|
|
23
|
+
config = ActiveAdminPrism.configuration
|
|
24
|
+
flashes_data = { "prism-transition-ms": config.flash_transition_ms.to_i }
|
|
25
|
+
if config.flash_auto_dismiss
|
|
26
|
+
flashes_data[:"prism-auto-dismiss-ms"] = (config.flash_auto_dismiss_seconds.to_f * 1000).round
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
div class: "flashes", data: flashes_data do
|
|
30
|
+
flash_messages.each do |type, messages|
|
|
31
|
+
[*messages].each do |message|
|
|
32
|
+
if config.flash_dismissible
|
|
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
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Read by prism.js / scss-src's CSS to decide whether to route
|
|
49
|
+
# data-confirm links through the styled dialog, whether the Filters
|
|
50
|
+
# sidebar panel collapses to an icon, and whether ActiveAdmin's
|
|
51
|
+
# original #footer should stay hidden (see
|
|
52
|
+
# ActiveAdminPrism::Configuration #styled_confirms /
|
|
53
|
+
# #collapsible_filters / #sidebar_footer).
|
|
54
|
+
#
|
|
55
|
+
# Can't call `super` here — #body_classes is defined directly on
|
|
56
|
+
# this same class (not a superclass), so reopening and redefining
|
|
57
|
+
# it replaces that definition outright; there's no ancestor
|
|
58
|
+
# implementation left for `super` to reach; (Arbre::Element#method_missing
|
|
59
|
+
# would silently redirect it right back to this same method,
|
|
60
|
+
# recursing forever). The body has to be copied instead.
|
|
61
|
+
def body_classes
|
|
62
|
+
config = ActiveAdminPrism.configuration
|
|
63
|
+
classes = Arbre::HTML::ClassList.new [
|
|
64
|
+
params[:action],
|
|
65
|
+
params[:controller].tr("/", "_"),
|
|
66
|
+
"active_admin", "logged_in",
|
|
67
|
+
active_admin_namespace.name.to_s + "_namespace"
|
|
68
|
+
]
|
|
69
|
+
classes << "prism-styled-confirms-disabled" unless config.styled_confirms
|
|
70
|
+
classes << "prism-filters-collapsible-disabled" unless config.collapsible_filters
|
|
71
|
+
classes << "prism-sidebar-footer-disabled" unless config.sidebar_footer
|
|
72
|
+
classes
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveAdmin
|
|
4
|
+
module Views
|
|
5
|
+
class IndexAsTable
|
|
6
|
+
class IndexTableFor
|
|
7
|
+
# ActiveAdmin's default View/Edit/Delete row links (built by
|
|
8
|
+
# IndexTableFor#defaults) already carry distinct classes —
|
|
9
|
+
# "view_link", "edit_link", "delete_link" — specifically so themes
|
|
10
|
+
# can hook into them. This swaps their text label for an icon +
|
|
11
|
+
# a visually-hidden label (kept for screen readers/tooltips),
|
|
12
|
+
# without touching the url/method/data-confirm behavior at all.
|
|
13
|
+
# Any other custom `item(...)` call (e.g. from a host's own
|
|
14
|
+
# `actions do |resource| item "Preview", ... end` block) is
|
|
15
|
+
# untouched, since it won't carry one of these classes.
|
|
16
|
+
class TableActions
|
|
17
|
+
ACTION_ICONS = {
|
|
18
|
+
"view_link" => :eye,
|
|
19
|
+
"edit_link" => :pencil,
|
|
20
|
+
"delete_link" => :trash
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
def item(*args, **kwargs)
|
|
24
|
+
unless ActiveAdminPrism.configuration.colorize_action_icons
|
|
25
|
+
return text_node helpers.link_to(*args, **kwargs)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
label = args[0]
|
|
29
|
+
url = args[1]
|
|
30
|
+
icon_name = ACTION_ICONS.values_at(*kwargs[:class].to_s.split).compact.first
|
|
31
|
+
|
|
32
|
+
unless icon_name && (icon = ActiveAdminPrism::Icons.svg(icon_name, css_class: "prism-action-icon"))
|
|
33
|
+
return text_node helpers.link_to(*args, **kwargs)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
content = helpers.safe_join([
|
|
37
|
+
icon.html_safe,
|
|
38
|
+
helpers.content_tag(:span, label, class: "prism-visually-hidden")
|
|
39
|
+
])
|
|
40
|
+
text_node helpers.link_to(content, url, **kwargs)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|