active_admin_prism 0.1.2 → 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.
data/scss-src/_base.scss CHANGED
@@ -57,11 +57,44 @@ a {
57
57
 
58
58
  // Flash messages — ActiveAdmin renders these with a heavy full-width
59
59
  // gradient bar (padding: 13px 30px 11px, font-size: 1.1em); this reskins
60
- // them into a compact, inset, color-coded banner. ".flashes" itself gets
61
- // its grid placement from _sidebar.scss (must be explicit see comment
62
- // there on why leaving it to auto-placement broke the layout).
60
+ // them into a floating toast stack, pinned to the viewport rather than
61
+ // pushed into the page's own grid flow (a toast that shifted #main_content
62
+ // down every time one appeared, then snapped back up when it auto-hid,
63
+ // would be its own layout-jank bug). ".flashes" still gets a grid-row/
64
+ // grid-column from _sidebar.scss for its notional *static* position (some
65
+ // tooling/AT expects every grid child to have one), but `position: fixed`
66
+ // below is what actually places it — relative to the viewport, ignoring
67
+ // that grid area entirely.
63
68
  .flashes {
64
- padding: 10px 10px 0;
69
+ position: fixed;
70
+ top: 20px;
71
+ right: 20px;
72
+ z-index: 1000;
73
+ display: flex;
74
+ flex-direction: column;
75
+ gap: 12px;
76
+ width: 340px;
77
+ max-width: calc(100vw - 24px);
78
+ padding: 0;
79
+ pointer-events: none;
80
+
81
+ // Individual toasts opt back into being interactive (hoverable/
82
+ // clickable dismiss button) — the wrapper itself must stay
83
+ // non-interactive, or it would sit as an invisible 340px-wide click
84
+ // blocker over the top-right corner of every page even between toasts.
85
+ > * {
86
+ pointer-events: auto;
87
+ }
88
+ }
89
+
90
+ @media (max-width: 480px) {
91
+ .flashes {
92
+ top: 12px;
93
+ right: 12px;
94
+ left: 12px;
95
+ width: auto;
96
+ max-width: none;
97
+ }
65
98
  }
66
99
 
67
100
  // "body.active_admin .flash" (not a flat ".flash") on purpose: ActiveAdmin
@@ -83,21 +116,25 @@ body.active_admin .flash {
83
116
  position: relative;
84
117
  display: flex;
85
118
  align-items: flex-start;
86
- gap: 12px;
87
- background: $prism-bg-page;
119
+ gap: 10px;
120
+ background: $prism-bg-card;
88
121
  border: 1px solid $prism-border-color;
89
- border-left: 3px solid $prism-text-muted;
90
- border-radius: $prism-radius-sm;
91
- box-shadow: none;
122
+ border-radius: $prism-radius;
123
+ // overflow: hidden clips .prism-flash-progress's square corners to this
124
+ // card's own rounded ones.
125
+ overflow: hidden;
126
+ box-shadow: $prism-shadow-popover;
92
127
  text-shadow: none;
93
128
  color: $prism-text-heading;
94
- font-size: 0.9em;
129
+ font-size: 0.88em;
95
130
  font-weight: 600;
96
131
  line-height: 1.4;
97
- padding: 10px 16px;
132
+ // Right padding clears .prism-flash-dismiss, absolutely positioned in
133
+ // this corner rather than sitting inline — see below.
134
+ padding: 14px 34px 16px 14px;
98
135
  margin-bottom: 0;
99
136
  opacity: 1;
100
- transform: translateY(0) scale(1);
137
+ transform: translateX(0) scale(1);
101
138
  // 320ms/cubic-bezier here matches Configuration#flash_transition_ms's
102
139
  // default and is overridden inline by prism.js (transition-duration) if a
103
140
  // host configures a different value — see that file's flash-dismiss IIFE.
@@ -109,47 +146,147 @@ body.active_admin .flash {
109
146
  // outright once the transition finishes.
110
147
  &.prism-flash-hide {
111
148
  opacity: 0;
112
- transform: translateY(-6px) scale(0.98);
149
+ transform: translateX(24px) scale(0.98);
113
150
  }
114
151
 
115
- &.flash_notice {
116
- background: $prism-color-success-light;
117
- border-left-color: $prism-color-success;
118
- color: darken($prism-color-success, 20%);
152
+ // "notice"/"success" both read as the positive/affirmative case (Rails'
153
+ // own convention already treats :notice that way) — same color, same
154
+ // :check_circle icon (see flash_messages.rb#FLASH_ICONS).
155
+ //
156
+ // background/border/color are redeclared here (not just left to the
157
+ // base .flash rule above) because ActiveAdmin core itself carries a
158
+ // "body.logged_in .flash.flash_notice"/".flash_error" rule of its own
159
+ // (active_admin/components/_flash_messages.scss — a pale gradient +
160
+ // colored border-bottom), and THAT selector is one class more specific
161
+ // than the base ".flash" rule above (0,3,1 vs 0,2,1) — more specific
162
+ // than "body.active_admin .flash" even though this gem's own class list
163
+ // ties AA's elsewhere. Only ".flash_notice"/".flash_error" get this
164
+ // treatment because those are the only two AA core actually special-
165
+ // cases; every other type here already wins on the base rule alone.
166
+ &.flash_notice,
167
+ &.flash_success {
168
+ background: $prism-bg-card;
169
+ border-bottom-color: $prism-border-color;
170
+ color: $prism-text-heading;
171
+
172
+ .prism-flash-icon {
173
+ color: $prism-color-success;
174
+ }
175
+
176
+ .prism-flash-progress {
177
+ background: $prism-color-success;
178
+ }
119
179
  }
120
180
 
121
181
  &.flash_error,
122
182
  &.flash_alert {
123
- background: $prism-color-danger-light;
124
- border-left-color: $prism-color-danger;
125
- color: darken($prism-color-danger, 10%);
183
+ background: $prism-bg-card;
184
+ border-bottom-color: $prism-border-color;
185
+ color: $prism-text-heading;
186
+
187
+ .prism-flash-icon {
188
+ color: $prism-color-danger;
189
+ }
190
+
191
+ .prism-flash-progress {
192
+ background: $prism-color-danger;
193
+ }
194
+ }
195
+
196
+ &.flash_warning {
197
+ .prism-flash-icon {
198
+ color: $prism-color-warning;
199
+ }
200
+
201
+ .prism-flash-progress {
202
+ background: $prism-color-warning;
203
+ }
204
+ }
205
+
206
+ // Any other flash key (a host's own custom one) falls back here too —
207
+ // flash_messages.rb#flash_icon already defaults to the same :info icon
208
+ // for anything not in FLASH_ICONS, so this default color matches it.
209
+ &.flash_info,
210
+ &:not(.flash_notice):not(.flash_success):not(.flash_error):not(.flash_alert):not(.flash_warning) {
211
+ .prism-flash-icon {
212
+ color: $prism-color-info;
213
+ }
214
+
215
+ .prism-flash-progress {
216
+ background: $prism-color-info;
217
+ }
126
218
  }
127
219
  }
128
220
 
221
+ .prism-flash-icon {
222
+ flex: 0 0 auto;
223
+ margin-top: 1px;
224
+ }
225
+
129
226
  .prism-flash-message {
130
227
  flex: 1 1 auto;
228
+ padding-top: 1px;
131
229
  }
132
230
 
133
231
  .prism-flash-dismiss {
232
+ position: absolute;
233
+ top: 10px;
234
+ right: 10px;
134
235
  flex: 0 0 auto;
135
236
  display: inline-flex;
136
237
  align-items: center;
137
238
  justify-content: center;
138
239
  width: 22px;
139
240
  height: 22px;
140
- margin: -2px -4px -2px 0;
141
241
  padding: 0;
142
242
  border: none;
143
243
  border-radius: $prism-radius-sm;
144
244
  background: transparent !important;
145
- color: inherit !important;
146
- opacity: 0.6;
245
+ color: $prism-text-muted !important;
246
+ opacity: 0.8;
147
247
  cursor: pointer;
148
248
  transition: opacity $prism-transition-fast, background $prism-transition-fast;
149
249
 
150
250
  &:hover {
151
251
  opacity: 1;
152
- background: rgba(0, 0, 0, 0.06) !important;
252
+ background: $prism-bg-page !important;
253
+ }
254
+ }
255
+
256
+ // The countdown bar along the card's bottom edge — only rendered when
257
+ // ActiveAdminPrism::Configuration#flash_auto_dismiss is on (see
258
+ // flash_messages.rb), so its presence already implies a duration to
259
+ // animate against. "--prism-flash-duration" is set inline per-flash
260
+ // (flash_messages.rb) rather than read from js-src/prism.js's own
261
+ // "data-prism-auto-dismiss-ms" (rendered once on the whole ".flashes"
262
+ // wrapper, not per card) — CSS can't read a JS-parsed value, so the same
263
+ // number is rendered twice rather than having JS set the animation
264
+ // itself. scaleX (not animating `width` directly) is what lets the
265
+ // browser run this on the compositor instead of laying out every frame.
266
+ .prism-flash-progress {
267
+ position: absolute;
268
+ left: 0;
269
+ right: 0;
270
+ bottom: 0;
271
+ height: 3px;
272
+ transform-origin: left center;
273
+ animation: prism-flash-countdown var(--prism-flash-duration, 5000ms) linear forwards;
274
+
275
+ // Paused (not hidden) rather than reset to full width — a flash a
276
+ // visitor is actively reading (mouse over it) shouldn't keep silently
277
+ // ticking down and vanish out from under them.
278
+ body.active_admin .flash:hover & {
279
+ animation-play-state: paused;
280
+ }
281
+ }
282
+
283
+ @keyframes prism-flash-countdown {
284
+ from {
285
+ transform: scaleX(1);
286
+ }
287
+
288
+ to {
289
+ transform: scaleX(0);
153
290
  }
154
291
  }
155
292
 
data/scss-src/_forms.scss CHANGED
@@ -20,6 +20,21 @@ form {
20
20
  font-weight: 600;
21
21
  font-size: 0.9em;
22
22
  }
23
+
24
+ // ActiveAdmin core's form layout floats each field row's <label>
25
+ // (float: left; width: 20% — active_admin's own _forms.scss) as its
26
+ // two-column label/input technique. Without a clearfix, a floated
27
+ // label's height doesn't count toward its own <li>'s box, so a short
28
+ // row right after one whose label this theme deliberately does NOT
29
+ // float (e.g. a boolean field, or `as: :prism_toggle` above) can let
30
+ // the *next* row's own floated label render up alongside the previous
31
+ // row instead of starting its own line below it. Same class of
32
+ // fragility the button row below already had (float has no effect on
33
+ // siblings once contained) — same fix here, kept to a plain clearfix
34
+ // rather than a full flex rewrite of every field row.
35
+ > ol > li {
36
+ overflow: hidden;
37
+ }
23
38
  }
24
39
 
25
40
  input[type="text"],
data/scss-src/_login.scss CHANGED
@@ -71,10 +71,11 @@ body.logged_out:has(.prism-login-brand) {
71
71
  margin: 0 auto;
72
72
  }
73
73
 
74
- .flashes {
75
- width: 100%;
76
- padding: 0 0 10px;
77
- }
74
+ // No login-specific ".flashes" override here (there used to be one,
75
+ // sizing it to the centered login card's own width) — .flashes is a
76
+ // fixed-position toast stack now (scss-src/_base.scss), anchored to the
77
+ // viewport corner rather than laid out inline above the card, so the
78
+ // same global sizing/position applies here unmodified too.
78
79
 
79
80
  #active_admin_content {
80
81
  width: 100%;
@@ -236,6 +236,167 @@ body.prism-filters-collapsible-disabled .prism-collapsible-panel {
236
236
  }
237
237
  }
238
238
 
239
+ // AA core's own ".table_tools" (active_admin/views/pages/index.rb,
240
+ // styled by its components/_table_tools.scss) is a clearfixed block whose
241
+ // children (the Batch Actions dropdown, scope tabs) float left. Flexbox
242
+ // is what puts .prism-active-filters-bar below on the same row — floats
243
+ // have no effect on flex items regardless of specificity/load order, same
244
+ // rationale as the form actions row in _forms.scss.
245
+ .table_tools {
246
+ display: flex;
247
+ flex-wrap: wrap;
248
+ align-items: center;
249
+ // row-gap only (not the shorthand "gap", i.e. no column-gap) —
250
+ // .prism-active-filters-bar below is pushed to this row's far end via
251
+ // `margin-left: auto`, not `justify-content`, and a nonzero column-gap
252
+ // on the same axis as that auto margin gets double-counted by the auto
253
+ // margin's own free-space math, leaving exactly one gap's worth of
254
+ // unclaimed space *after* the last item instead of before it — the bar
255
+ // fell short of the table's own right edge by exactly 12px. row-gap
256
+ // still spaces .table_tools_actions from the bar vertically if they
257
+ // wrap onto separate lines on a narrow viewport, without that bug.
258
+ row-gap: 12px;
259
+ }
260
+
261
+ // New wrapper (not AA's own markup) around whatever .table_tools would
262
+ // have rendered directly before — see
263
+ // lib/active_admin/views/active_filters_bar.rb#build_table_tools. Only
264
+ // present when any_table_tools? is true.
265
+ .table_tools_actions {
266
+ display: flex;
267
+ flex-wrap: wrap;
268
+ align-items: center;
269
+ gap: 10px;
270
+ }
271
+
272
+ // Compact "current scope + active filters" bar, rendered beside the Batch
273
+ // Actions button/scope tabs instead of ActiveAdmin core's own
274
+ // #sidebar-based "Search status" panel — see
275
+ // lib/active_admin/views/active_filters_bar.rb (Ruby side,
276
+ // ActiveAdminPrism::Configuration#active_filters_bar) for why: that
277
+ // sidebar panel is a child of #sidebar, so it gets squeezed down to the
278
+ // same 72px icon gutter as the Filters form whenever collapsible_filters
279
+ // has it collapsed, and its content (one "field contains value" sentence
280
+ // per active filter) has nowhere to fit in 72px and overflows out over
281
+ // the table instead of shrinking cleanly. This reuses that same Ruby
282
+ // component's markup/i18n (ActiveAdmin::Views::ActiveFiltersSidebarContent)
283
+ // wholesale — restyled here as a single horizontal row of pill tags
284
+ // instead of a vertical sidebar list, rather than reimplemented.
285
+ //
286
+ // Given its own tinted background/border (the theme's primary color,
287
+ // same tokens as an active nav item or a focused input's ring) rather
288
+ // than a plain card, so it reads as a distinct, "something is filtered"
289
+ // signal at a glance next to the neutral Batch Actions button beside it —
290
+ // not just another gray panel a visitor could mistake for chrome.
291
+ .prism-active-filters-bar {
292
+ // inline-flex (not the plain block a bare `div` defaults to) sizes this
293
+ // to its own content instead of stretching to fill .table_tools's
294
+ // remaining width. margin-left: auto pushes it to the row's far end
295
+ // regardless of whether .table_tools_actions exists beside it (only
296
+ // rendered `if any_table_tools?` — a resource with filters active but
297
+ // no batch actions/scopes has nothing else in this row at all).
298
+ display: inline-flex;
299
+ max-width: 100%;
300
+ margin-left: auto;
301
+ padding: 8px 14px;
302
+ background: $prism-color-primary-light;
303
+ border: 1px solid rgba($prism-color-primary, 0.3);
304
+ border-left: 3px solid $prism-color-primary;
305
+ border-radius: $prism-radius;
306
+ font-size: 0.85em;
307
+ // Clicking it opens the Filters panel (js-src/prism.js) — a plain
308
+ // hover/focus cue rather than restyling the whole card on hover, so it
309
+ // doesn't fight with the already-deliberate "eye-catching" tinted look.
310
+ cursor: pointer;
311
+ transition: box-shadow $prism-transition-fast;
312
+
313
+ &:hover,
314
+ &:focus-visible {
315
+ box-shadow: 0 0 0 3px rgba($prism-color-primary, 0.15);
316
+ }
317
+
318
+ &:focus-visible {
319
+ outline: none;
320
+ }
321
+
322
+ // The builder_method wraps ActiveFiltersSidebarContent's own output in a
323
+ // ".active_filters_sidebar_content" div (Arbre::Component's own
324
+ // convention) — this, not .prism-active-filters-bar itself, is the
325
+ // direct parent of the "Scope:"/"Current filters:" markup below.
326
+ .active_filters_sidebar_content {
327
+ display: flex;
328
+ flex-wrap: wrap;
329
+ align-items: center;
330
+ gap: 8px 14px;
331
+ }
332
+
333
+ // ActiveFiltersSidebarContent#scope_block's "Scope:" <h4> (only present
334
+ // when a scope is active) + its value <b class="current_scope_name">.
335
+ > .active_filters_sidebar_content > h4 {
336
+ margin: 0;
337
+ font-size: 1em;
338
+ font-weight: 700;
339
+ text-transform: uppercase;
340
+ letter-spacing: 0.04em;
341
+ color: $prism-color-primary-dark;
342
+ }
343
+
344
+ .current_scope_name {
345
+ color: $prism-color-primary-dark;
346
+ font-weight: 700;
347
+ }
348
+
349
+ // #filters_list wraps its own "Current filters:" <h4> + <ul> in a
350
+ // `div style="margin-top: 10px"`, sized for stacking under the scope
351
+ // line in a vertical sidebar — reset that now that everything sits
352
+ // inline in the same row instead.
353
+ > .active_filters_sidebar_content > div {
354
+ display: flex;
355
+ flex-wrap: wrap;
356
+ align-items: center;
357
+ gap: 8px 14px;
358
+ margin-top: 0 !important;
359
+
360
+ > h4 {
361
+ margin: 0 !important;
362
+ font-size: 1em;
363
+ font-weight: 700;
364
+ text-transform: uppercase;
365
+ letter-spacing: 0.04em;
366
+ color: $prism-color-primary-dark;
367
+ }
368
+
369
+ > ul {
370
+ display: flex;
371
+ flex-wrap: wrap;
372
+ gap: 6px;
373
+ margin: 0;
374
+ padding: 0;
375
+ list-style: none;
376
+
377
+ li {
378
+ display: inline-flex;
379
+ align-items: center;
380
+ gap: 4px;
381
+ padding: 4px 10px;
382
+ border-radius: $prism-radius-pill;
383
+ background: $prism-bg-card;
384
+ box-shadow: 0 1px 2px rgba(16, 24, 40, 0.06);
385
+ line-height: 1.4;
386
+
387
+ span {
388
+ color: $prism-text-muted;
389
+ }
390
+
391
+ b {
392
+ font-weight: 700;
393
+ color: $prism-color-primary-dark;
394
+ }
395
+ }
396
+ }
397
+ }
398
+ }
399
+
239
400
  .blank_slate_container {
240
401
  .blank_slate {
241
402
  background: $prism-bg-card;
@@ -201,19 +201,36 @@ form.filter_form .filter_form_field.select_and_search .select2-container {
201
201
 
202
202
  // Main "fieldset.inputs" form: ActiveAdmin's own _forms.scss floats the
203
203
  // label left at 20% width and gives text inputs an explicit
204
- // width: calc(80% - padding) so they sit in the remaining space beside it
205
- // a value this gem has no access to at compile time (it's compiled
204
+ // width: calc(80% - 22px) so they sit in the remaining space beside it
205
+ // a value this gem has no access to at compile time (it's compiled
206
206
  // standalone, without AA's own SCSS variables/mixins in its load path; see
207
- // bin/build-css). Rather than hardcode an equivalent, giving the container
208
- // its own block formatting context (overflow: hidden, no explicit width)
209
- // gets the same result for free: per the CSS spec, a BFC-establishing box
210
- // must not overlap a preceding float's margin box, so the browser sizes it
211
- // to exactly the remaining space automatically. Without this, the default
212
- // 100% width above would render full-width *underneath* the floated
213
- // label, visually painting over it this is the fix for that, not just a
214
- // visual nicety.
215
- fieldset.inputs .select2-container {
216
- overflow: hidden;
207
+ // bin/build-css), so it's matched here as a literal instead.
208
+ //
209
+ // A block-formatting-context trick (overflow: hidden, no explicit width)
210
+ // looks like it should get the same result for free — per the CSS spec, a
211
+ // BFC-establishing box must not overlap a preceding float's margin box —
212
+ // but it only actually sizes the box when its width is auto. Select2 sets
213
+ // an inline `width: 100%` directly on `.select2-container` itself
214
+ // (prism.js's own auto-init passes `{ width: "100%" }`, and jQuery's
215
+ // .css("width", ...) call is what select2.js uses under the hood even for
216
+ // a host's own manual `.select2()` call left at its default width option),
217
+ // so that inline 100% is never auto and the BFC sizing never kicks in —
218
+ // the container renders full-width *underneath* the floated label instead
219
+ // of beside it. `!important` is required here to beat that inline style.
220
+ form fieldset.inputs li.select > .select2-container {
221
+ display: inline-block;
222
+ width: calc(80% - 22px) !important;
223
+ vertical-align: middle;
224
+ }
225
+
226
+ // Any select Select2 has not (yet) enhanced — it hides the real one via a
227
+ // 1px offscreen technique once active, so this only matches before that,
228
+ // or if it's disabled/skipped entirely — needs the same geometry so the
229
+ // row doesn't jump once Select2 does attach.
230
+ form fieldset.inputs li.select > select:not(.select2-hidden-accessible) {
231
+ display: inline-block;
232
+ width: calc(80% - 22px);
233
+ vertical-align: middle;
217
234
  }
218
235
 
219
236
  // The dropdown panel itself is appended to <body> (or wherever