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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +133 -0
  3. data/INTEGRATION.md +1012 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +319 -0
  6. data/app/assets/javascripts/active_admin_prism/prism.js +1 -0
  7. data/app/assets/stylesheets/active_admin_prism/_variable_overrides.scss +43 -0
  8. data/app/assets/stylesheets/active_admin_prism/prism.css +1 -0
  9. data/app/views/active_admin/devise/confirmations/new.html.erb +26 -0
  10. data/app/views/active_admin/devise/passwords/edit.html.erb +29 -0
  11. data/app/views/active_admin/devise/passwords/new.html.erb +26 -0
  12. data/app/views/active_admin/devise/registrations/new.html.erb +32 -0
  13. data/app/views/active_admin/devise/sessions/new.html.erb +37 -0
  14. data/app/views/active_admin/devise/shared/_brand.html.erb +34 -0
  15. data/app/views/active_admin/devise/unlocks/new.html.erb +25 -0
  16. data/js-src/prism.js +270 -0
  17. data/lib/active_admin/views/filters_sidebar.rb +69 -0
  18. data/lib/active_admin/views/flash_messages.rb +77 -0
  19. data/lib/active_admin/views/index_table_actions.rb +46 -0
  20. data/lib/active_admin/views/prism_sidebar.rb +212 -0
  21. data/lib/active_admin_prism/configuration.rb +180 -0
  22. data/lib/active_admin_prism/engine.rb +18 -0
  23. data/lib/active_admin_prism/version.rb +5 -0
  24. data/lib/active_admin_prism.rb +33 -0
  25. data/lib/formtastic/inputs/prism_toggle_input.rb +31 -0
  26. data/lib/generators/active_admin_prism/error_pages/error_pages_generator.rb +49 -0
  27. data/lib/generators/active_admin_prism/error_pages/templates/404.html +164 -0
  28. data/lib/generators/active_admin_prism/error_pages/templates/500.html +173 -0
  29. data/lib/generators/active_admin_prism/install/install_generator.rb +69 -0
  30. data/lib/prism_icons.rb +82 -0
  31. data/lib/prism_login_helper.rb +31 -0
  32. data/lib/prism_toggle_tag.rb +35 -0
  33. data/scss-src/_base.scss +281 -0
  34. data/scss-src/_buttons.scss +134 -0
  35. data/scss-src/_forms.scss +272 -0
  36. data/scss-src/_icons.scss +27 -0
  37. data/scss-src/_login.scss +306 -0
  38. data/scss-src/_panels.scss +249 -0
  39. data/scss-src/_sidebar.scss +479 -0
  40. data/scss-src/_tables.scss +142 -0
  41. data/scss-src/_variables.scss +101 -0
  42. data/scss-src/prism.scss +15 -0
  43. metadata +157 -0
@@ -0,0 +1,306 @@
1
+ // Devise auth pages (sign in, sign up, forgot/reset password, resend
2
+ // confirmation, resend unlock) — see the shared
3
+ // app/views/active_admin/devise/shared/_brand.html.erb partial for the
4
+ // markup this targets (rendered by every action's own view — sessions/new,
5
+ // registrations/new, passwords/new, passwords/edit, unlocks/new,
6
+ // confirmations/new), and ActiveAdminPrism::Configuration#login_page/
7
+ // #login_logo/#login_app_name/#login_tagline for the config behind it
8
+ // (login_tagline only actually renders on sessions#new — see the partial).
9
+ //
10
+ // Scoped under "body.logged_out:has(.prism-login-brand)" rather than a
11
+ // plain "body.logged_out" so that config.login_page = false — which skips
12
+ // rendering .prism-login-brand entirely — falls back to ActiveAdmin's
13
+ // completely untouched stock look on every one of these pages with no
14
+ // separate "disabled" CSS needed here. Same :has()-without-support caveat
15
+ // as the Filters-panel reflow in _panels.scss: browsers lacking :has()
16
+ // just keep AA's default appearance instead of Prism's card/blob treatment.
17
+ body.logged_out:has(.prism-login-brand) {
18
+ min-height: 100vh;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ background: $prism-bg-page;
23
+ overflow: hidden;
24
+ position: relative;
25
+
26
+ // Soft animated background blobs — pure CSS radial-gradient pseudo
27
+ // elements on <body> itself, no extra markup or JS required.
28
+ &::before,
29
+ &::after {
30
+ content: "";
31
+ position: fixed;
32
+ z-index: 0;
33
+ border-radius: 50%;
34
+ filter: blur(70px);
35
+ opacity: 0.35;
36
+ pointer-events: none;
37
+ }
38
+
39
+ &::before {
40
+ width: 420px;
41
+ height: 420px;
42
+ top: -140px;
43
+ left: -120px;
44
+ background: radial-gradient(circle at 30% 30%, $prism-color-primary, transparent 70%);
45
+ animation: prism-login-blob-a 16s ease-in-out infinite;
46
+ }
47
+
48
+ &::after {
49
+ width: 360px;
50
+ height: 360px;
51
+ bottom: -120px;
52
+ right: -100px;
53
+ background: radial-gradient(circle at 70% 70%, $prism-color-primary-light, transparent 70%);
54
+ animation: prism-login-blob-b 18s ease-in-out infinite;
55
+ }
56
+
57
+ #wrapper {
58
+ position: relative;
59
+ z-index: 1;
60
+ display: block;
61
+ min-height: 0;
62
+ width: auto;
63
+ }
64
+
65
+ #content_wrapper {
66
+ display: flex;
67
+ flex-direction: column;
68
+ align-items: center;
69
+ width: 400px;
70
+ max-width: 90vw;
71
+ margin: 0 auto;
72
+ }
73
+
74
+ .flashes {
75
+ width: 100%;
76
+ padding: 0 0 10px;
77
+ }
78
+
79
+ #active_admin_content {
80
+ width: 100%;
81
+ box-sizing: border-box;
82
+ background: $prism-bg-card;
83
+ border: 1px solid $prism-border-color;
84
+ border-radius: $prism-radius;
85
+ box-shadow: $prism-shadow-popover;
86
+ padding: 40px 36px 32px;
87
+ }
88
+
89
+ #footer {
90
+ position: relative;
91
+ z-index: 1;
92
+ border-top: none;
93
+ text-align: center;
94
+ }
95
+
96
+ // _forms.scss's "form fieldset.inputs" gives every fieldset its own
97
+ // white card chrome (border + border-radius) — right everywhere else,
98
+ // where a fieldset sits directly on the gray page background, but
99
+ // redundant here: the fieldset already sits inside #active_admin_content
100
+ // (itself a white card, above). ActiveAdmin's own login-page reset
101
+ // (active_admin/pages/_logged_out.scss's "body.logged_out #login form
102
+ // fieldset") already zeroes background/padding/box-shadow but never
103
+ // touches border/border-radius, so without this, the card-in-a-card
104
+ // outline was the "too many borders" this rule fixes.
105
+ #login fieldset.inputs {
106
+ border: none;
107
+ border-radius: 0;
108
+ }
109
+
110
+ // The submit button (Login/Sign up/Reset My Password/...) defaults to
111
+ // a flush-left button, out of step with the centered brand block above
112
+ // and the centered link chips below. A true full-width button (an
113
+ // earlier version of this rule) fixed the alignment but read as
114
+ // oversized next to the (narrower) input fields above it — this keeps
115
+ // it an intentionally-sized, centered button instead: same visual
116
+ // weight as the fields above rather than stretched edge-to-edge.
117
+ //
118
+ // "float: none" on the li is still required for text-align: center to
119
+ // have any effect: ActiveAdmin itself floats that wrapping <li> ("form
120
+ // fieldset.buttons li, form fieldset.actions li { float: left; padding:
121
+ // 0; }", active_admin/_forms.scss) — a floated box establishes its own
122
+ // block formatting context and ignores an ancestor's text-align
123
+ // entirely, so centering never took effect (and a floated box also
124
+ // shrink-wraps to content width regardless of any width set on its
125
+ // child) until this is unset.
126
+ #login fieldset.actions {
127
+ margin-top: 4px;
128
+ text-align: center;
129
+
130
+ li {
131
+ float: none;
132
+ }
133
+
134
+ // AA's own rule zeroes this li's padding outright ("form
135
+ // fieldset.buttons li, form fieldset.actions li { ... padding: 0;
136
+ // }") — without breathing room here, the button reads as jammed
137
+ // right up against the fields above and the link chips below.
138
+ //
139
+ // margin-left nudges the centered button to line up with the actual
140
+ // input columns above it, rather than with the full card width
141
+ // (label+input combined) — "form fieldset ol > li label" gives every
142
+ // label above a floated column, so the fields themselves are only
143
+ // ever centered within the remaining space to the label's right.
144
+ li.input_action {
145
+ padding: 20px 0;
146
+ margin-left: 30px;
147
+ margin-bottom: 15px;
148
+ }
149
+
150
+ input[type="submit"] {
151
+ display: inline-block;
152
+ width: auto;
153
+ min-width: 200px;
154
+ padding: 12px 28px;
155
+ font-size: 0.95em;
156
+ }
157
+ }
158
+
159
+ // ActiveAdmin's shared devise/shared/_error_messages.html.erb partial
160
+ // (rendered by every action except sessions#new, which uses its own
161
+ // flash instead) — reskinned into a compact card matching the flash
162
+ // banners rather than AA's plain bulleted list.
163
+ #error_explanation {
164
+ background: $prism-color-danger-light;
165
+ border: 1px solid rgba($prism-color-danger, 0.3);
166
+ border-radius: $prism-radius-sm;
167
+ padding: 12px 16px;
168
+ margin-bottom: 20px;
169
+ color: darken($prism-color-danger, 10%);
170
+
171
+ h2 {
172
+ background: none !important;
173
+ box-shadow: none !important;
174
+ text-shadow: none !important;
175
+ border: none !important;
176
+ margin: 0 0 6px !important;
177
+ padding: 0 !important;
178
+ color: inherit;
179
+ font-size: 0.95em;
180
+ font-weight: 700;
181
+ }
182
+
183
+ ul {
184
+ margin: 0;
185
+ padding-left: 18px;
186
+ font-size: 0.88em;
187
+ font-weight: 600;
188
+ }
189
+ }
190
+ }
191
+
192
+ @keyframes prism-login-blob-a {
193
+ 0%, 100% { transform: translate(0, 0) scale(1); }
194
+ 50% { transform: translate(60px, 40px) scale(1.1); }
195
+ }
196
+
197
+ @keyframes prism-login-blob-b {
198
+ 0%, 100% { transform: translate(0, 0) scale(1); }
199
+ 50% { transform: translate(-50px, -30px) scale(1.08); }
200
+ }
201
+
202
+ .prism-login-brand {
203
+ text-align: center;
204
+ margin-bottom: 28px;
205
+ }
206
+
207
+ .prism-login-logo {
208
+ width: 64px;
209
+ height: 64px;
210
+ margin: 0 auto 14px;
211
+ color: $prism-color-primary;
212
+ }
213
+
214
+ .prism-login-mark {
215
+ display: block;
216
+ width: 100%;
217
+ height: 100%;
218
+ }
219
+
220
+ .prism-login-beam {
221
+ stroke-dasharray: 40;
222
+ animation: prism-login-beam-flow 2.4s linear infinite;
223
+ }
224
+
225
+ @keyframes prism-login-beam-flow {
226
+ to { stroke-dashoffset: -80; }
227
+ }
228
+
229
+ .prism-login-tri {
230
+ transform-origin: center;
231
+ animation: prism-login-tri-glow 3.2s ease-in-out infinite;
232
+ }
233
+
234
+ @keyframes prism-login-tri-glow {
235
+ 0%, 100% { filter: drop-shadow(0 0 0 rgba(0, 0, 0, 0)); }
236
+ 50% { filter: drop-shadow(0 0 6px rgba($prism-color-primary, 0.45)); }
237
+ }
238
+
239
+ // Beats "body.logged_out h2" (ActiveAdmin's own gradient-header rule, in
240
+ // active_admin/pages/_logged_out.scss) via the "#login" id rather than a
241
+ // flat class selector — same specificity approach used throughout this
242
+ // gem. Scoped to ".prism-login-brand h2" specifically (not "#login h2")
243
+ // so the plain-text fallback heading rendered when config.login_page is
244
+ // false — which lives directly under #login, not under
245
+ // .prism-login-brand — is completely untouched and keeps AA's own look.
246
+ body.logged_out #login .prism-login-brand h2 {
247
+ background: none !important;
248
+ box-shadow: none !important;
249
+ text-shadow: none !important;
250
+ border: none !important;
251
+ margin: 0 !important;
252
+ padding: 0 !important;
253
+ color: $prism-text-heading;
254
+ }
255
+
256
+ .prism-login-title {
257
+ font-size: 1.6em;
258
+ font-weight: 800;
259
+ letter-spacing: -0.01em;
260
+ }
261
+
262
+ .prism-login-tagline {
263
+ margin: 4px 0 0;
264
+ color: $prism-text-muted;
265
+ font-size: 0.9em;
266
+ }
267
+
268
+ // Sign-up/forgot-password/etc links
269
+ // (active_admin/devise/shared/_links.erb) — ActiveAdmin renders them as a
270
+ // stack of "<a> <br>" pairs; turn that into a row of small pill chips
271
+ // instead (no divider line needed above them — the button-like chips read
272
+ // as a distinct group on their own, one less "hr" competing with the
273
+ // fieldset/card boundaries above them).
274
+ .prism-login-links {
275
+ margin-top: 24px;
276
+ display: flex;
277
+ flex-wrap: wrap;
278
+ justify-content: center;
279
+ gap: 10px;
280
+ text-align: center;
281
+
282
+ a {
283
+ display: inline-flex;
284
+ align-items: center;
285
+ padding: 7px 14px;
286
+ border: 1px solid $prism-border-color;
287
+ border-radius: $prism-radius-pill;
288
+ background: $prism-bg-page;
289
+ color: $prism-text-body;
290
+ font-size: 0.82em;
291
+ font-weight: 600;
292
+ text-decoration: none;
293
+ transition: background $prism-transition-fast, border-color $prism-transition-fast,
294
+ color $prism-transition-fast;
295
+
296
+ &:hover {
297
+ background: $prism-color-primary-light;
298
+ border-color: $prism-color-primary;
299
+ color: $prism-color-primary-dark;
300
+ }
301
+ }
302
+
303
+ br {
304
+ display: none;
305
+ }
306
+ }
@@ -0,0 +1,249 @@
1
+ // Card-style panels/columns for index & dashboard pages.
2
+ .panel {
3
+ background: $prism-bg-card;
4
+ border: 1px solid $prism-border-color;
5
+ border-radius: $prism-radius;
6
+ box-shadow: $prism-shadow-card;
7
+ margin-bottom: 20px;
8
+ overflow: hidden;
9
+
10
+ > h3 {
11
+ margin: 0;
12
+ padding: 16px 20px;
13
+ background: transparent;
14
+ border-bottom: 1px solid $prism-border-color;
15
+ color: $prism-text-heading;
16
+ font-size: 1em;
17
+ font-weight: 600;
18
+ }
19
+
20
+ .panel_contents {
21
+ padding: 20px;
22
+ }
23
+
24
+ .header_action {
25
+ float: right;
26
+ }
27
+ }
28
+
29
+ .columns {
30
+ display: flex;
31
+ flex-wrap: wrap;
32
+ gap: 20px;
33
+ margin: 0;
34
+
35
+ .column {
36
+ flex: 1 1 260px;
37
+ min-width: 0;
38
+ }
39
+ }
40
+
41
+ // ActiveAdmin's own right-hand content sidebar (the "Filters" panel is
42
+ // rendered in here, id="sidebar" — not to be confused with Prism's own
43
+ // left-hand nav, which is #header.prism-sidebar). Its width is fixed
44
+ // (AA's own $sidebar-width, see README for widening it), so this is a
45
+ // safety clamp against any content overflowing/clipping past its edge.
46
+ #sidebar {
47
+ box-sizing: border-box;
48
+ overflow-x: hidden;
49
+
50
+ .panel {
51
+ box-sizing: border-box;
52
+ }
53
+ }
54
+
55
+ // Reflow #main_content (the table/grid) and #sidebar's own width to match
56
+ // whether the collapsible Filters panel is expanded or not — driven by a
57
+ // "prism-filters-open" class prism.js toggles on <body> in lockstep with
58
+ // #filters_sidebar_section's own ".open" (a body class is necessary since
59
+ // #main_content is a *sibling* of #sidebar, not a descendant — CSS can't
60
+ // reach across siblings any other way). Both of ActiveAdmin's own rules
61
+ // here are triple-ID-scoped / double-ID-scoped
62
+ // (structure/_main_structure.scss), so matching that path (rather than a
63
+ // flatter selector) is what lets this reliably win regardless of load order.
64
+ $prism-filters-collapsed-gutter: 72px !default;
65
+
66
+ #active_admin_content #main_content_wrapper #main_content,
67
+ #active_admin_content #sidebar {
68
+ transition: margin-right 280ms cubic-bezier(0.4, 0, 0.2, 1),
69
+ width 280ms cubic-bezier(0.4, 0, 0.2, 1), margin-left 280ms cubic-bezier(0.4, 0, 0.2, 1);
70
+ }
71
+
72
+ body:has(.prism-collapsible-panel):not(.prism-filters-open) {
73
+ #active_admin_content:not(.without_sidebar) #main_content_wrapper #main_content {
74
+ margin-right: $prism-filters-collapsed-gutter;
75
+ }
76
+
77
+ #active_admin_content #sidebar {
78
+ width: $prism-filters-collapsed-gutter;
79
+ margin-left: -#{$prism-filters-collapsed-gutter};
80
+ }
81
+ }
82
+
83
+ // "Filters" sidebar panel — collapses to a single icon button by default
84
+ // (ActiveAdminPrism::Configuration#collapsible_filters), expanding to
85
+ // the full form on click (see prism.js's filters-collapse handler, and
86
+ // lib/active_admin/views/filters_sidebar.rb for the icon + label-span
87
+ // markup this targets). AA gives this specific SidebarSection a stable id
88
+ // ("#{name}_sidebar_section" => "filters_sidebar_section"), so this is
89
+ // scoped to it alone — any other panel (dashboard panels, custom sidebar
90
+ // sections a host adds) is untouched.
91
+ .prism-collapsible-panel {
92
+ // Collapsed (default): no floating white card — just the icon button —
93
+ // so it doesn't read as a stray tiny panel. Restored in full under
94
+ // "&.open" below.
95
+ background: transparent;
96
+ border-color: transparent;
97
+ box-shadow: none;
98
+ margin-bottom: 10px;
99
+ transition: background $prism-transition-fast, border-color $prism-transition-fast,
100
+ box-shadow $prism-transition-fast, margin $prism-transition-fast;
101
+
102
+ > h3 {
103
+ display: flex;
104
+ align-items: center;
105
+ gap: 8px;
106
+ width: 40px;
107
+ height: 40px;
108
+ margin: 0 0 0 auto;
109
+ padding: 0;
110
+ justify-content: center;
111
+ border: 1px solid $prism-border-color;
112
+ border-radius: $prism-radius-sm;
113
+ background: $prism-bg-card;
114
+ box-shadow: $prism-shadow-card;
115
+ color: $prism-text-heading;
116
+ cursor: pointer;
117
+ user-select: none;
118
+ overflow: hidden;
119
+ transition: width $prism-transition-fast, padding $prism-transition-fast,
120
+ justify-content $prism-transition-fast, background $prism-transition-fast,
121
+ border-color $prism-transition-fast;
122
+
123
+ &:hover {
124
+ background: $prism-color-primary-light;
125
+ color: $prism-color-primary-dark;
126
+ }
127
+ }
128
+
129
+ .prism-filter-icon {
130
+ flex: 0 0 auto;
131
+ // Flexbox `order` places the icon first visually regardless of DOM
132
+ // order — filters_sidebar.rb appends it after the label span, since
133
+ // Arbre has no clean API to prepend before an already-built child.
134
+ order: -1;
135
+ }
136
+
137
+ // Visually hidden (not display:none — screen readers, and the label
138
+ // still needs real layout box for the max-width transition below) while
139
+ // collapsed; restored to normal under "&.open".
140
+ .prism-filter-label {
141
+ max-width: 0;
142
+ overflow: hidden;
143
+ opacity: 0;
144
+ white-space: nowrap;
145
+ transition: max-width 200ms ease, opacity 150ms ease;
146
+ }
147
+
148
+ .panel_contents {
149
+ overflow: hidden;
150
+ max-height: 0;
151
+ opacity: 0;
152
+ padding-top: 0;
153
+ padding-bottom: 0;
154
+ transition: max-height 280ms cubic-bezier(0.4, 0, 0.2, 1), opacity 200ms ease,
155
+ padding 200ms ease;
156
+ }
157
+
158
+ &.open {
159
+ background: $prism-bg-card;
160
+ border-color: $prism-border-color;
161
+ box-shadow: $prism-shadow-card;
162
+ margin-bottom: 20px;
163
+
164
+ > h3 {
165
+ width: 100%;
166
+ height: auto;
167
+ margin: 0;
168
+ justify-content: flex-start;
169
+ padding: 16px 20px;
170
+ border: none;
171
+ border-bottom: 1px solid $prism-border-color;
172
+ background: transparent;
173
+ box-shadow: none;
174
+ color: $prism-text-heading;
175
+ border-radius: 0;
176
+
177
+ &:hover {
178
+ background: transparent;
179
+ color: $prism-text-heading;
180
+ }
181
+ }
182
+
183
+ .prism-filter-label {
184
+ max-width: 200px;
185
+ opacity: 1;
186
+ }
187
+
188
+ .panel_contents {
189
+ max-height: 1200px;
190
+ opacity: 1;
191
+ padding-top: 20px;
192
+ padding-bottom: 20px;
193
+ }
194
+ }
195
+ }
196
+
197
+ // Backstop for ActiveAdminPrism::Configuration#collapsible_filters =
198
+ // false: always show the full panel, matching ActiveAdmin's own default
199
+ // (prism.js also skips attaching the click handler in this case, and
200
+ // filters_sidebar.rb skips adding the icon/label markup entirely, so this
201
+ // rule is mostly a no-op safety net rather than the primary mechanism).
202
+ body.prism-filters-collapsible-disabled .prism-collapsible-panel {
203
+ background: $prism-bg-card;
204
+ border-color: $prism-border-color;
205
+ box-shadow: $prism-shadow-card;
206
+ margin-bottom: 20px;
207
+
208
+ > h3 {
209
+ width: 100%;
210
+ height: auto;
211
+ margin: 0;
212
+ justify-content: flex-start;
213
+ padding: 16px 20px;
214
+ border: none;
215
+ border-bottom: 1px solid $prism-border-color;
216
+ box-shadow: none;
217
+ cursor: default;
218
+ }
219
+
220
+ .panel_contents {
221
+ max-height: none;
222
+ opacity: 1;
223
+ overflow: visible;
224
+ padding: 20px;
225
+ }
226
+ }
227
+
228
+ .blank_slate_container {
229
+ .blank_slate {
230
+ background: $prism-bg-card;
231
+ border: 1px dashed $prism-border-color-strong;
232
+ border-radius: $prism-radius;
233
+ color: $prism-text-muted;
234
+ font-weight: 600;
235
+ box-shadow: none;
236
+ }
237
+ }
238
+
239
+ // Admin notes (ActiveAdmin::Comment) panel — author name/meta default to
240
+ // AA's grayscale $primary-color; bring them onto the theme's palette.
241
+ .comments {
242
+ .active_admin_comment_meta {
243
+ color: $prism-text-muted;
244
+
245
+ .active_admin_comment_author {
246
+ color: $prism-text-heading;
247
+ }
248
+ }
249
+ }