Skip to content

Commit 99af101

Browse files
committed
Keep form actions visible while editing long forms
Use a Stimulus controller to detect when the actions fieldset is stuck and apply elevated styling only in that state. This keeps save controls accessible at the bottom of long admin forms without making the footer look pinned when it is already in view.
1 parent 3c6c5aa commit 99af101

3 files changed

Lines changed: 46 additions & 1 deletion

File tree

app/assets/tailwind/components/formtastic.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,23 @@
257257
@apply mt-3 flex justify-end;
258258
}
259259

260+
.actions {
261+
@apply sticky bottom-0 z-10;
262+
}
263+
260264
.actions > ol {
261265
@apply flex flex-row-reverse items-center gap-4;
262266
}
263267

268+
.actions.is-stuck > ol {
269+
@apply -mx-5 rounded-tl-xl bg-[#FCFEFC] p-2.5 px-5 dark:border-t dark:border-l dark:border-gray-800 dark:bg-[#030712];
270+
box-shadow: 0 -4px 16px rgb(0 0 0 / 0.08);
271+
272+
:where(.dark, .dark *) & {
273+
box-shadow: 0 -4px 16px rgb(0 0 0 / 0.4);
274+
}
275+
}
276+
264277
/* Hints and Descriptions */
265278
.inline-hints {
266279
@apply mt-2 text-sm text-gray-500;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Controller } from "@hotwired/stimulus"
2+
3+
export default class extends Controller {
4+
connect() {
5+
this.observers = []
6+
this.element
7+
.querySelectorAll(".formtastic fieldset.actions")
8+
.forEach((fieldset) => this.observe(fieldset))
9+
}
10+
11+
disconnect() {
12+
this.observers.forEach(({ observer, sentinel }) => {
13+
observer.disconnect()
14+
sentinel.remove()
15+
})
16+
this.observers = []
17+
}
18+
19+
observe(fieldset) {
20+
const sentinel = document.createElement("div")
21+
sentinel.setAttribute("aria-hidden", "true")
22+
sentinel.style.height = "1px"
23+
sentinel.style.pointerEvents = "none"
24+
fieldset.after(sentinel)
25+
26+
const observer = new IntersectionObserver(([entry]) => {
27+
fieldset.classList.toggle("is-stuck", !entry.isIntersecting)
28+
})
29+
observer.observe(sentinel)
30+
this.observers.push({ observer, sentinel })
31+
}
32+
}

app/views/layouts/active_admin.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
grid grow grid-cols-1 gap-4 px-2.5 lg:auto-cols-[minmax(0,250px)]
2727
lg:grid-flow-col lg:gap-6 lg:px-5
2828
"
29-
data-controller="form-scroll-to-error"
29+
data-controller="form-scroll-to-error form-sticky-actions"
3030
data-action="turbo:submit-start->form-scroll-to-error#observeForms"
3131
>
3232
<%= yield %>

0 commit comments

Comments
 (0)