Tags
This is such an improvement to the backend of my website it warrants a blog post of celebration. For far too long I've been dealing with a tiny ass edit form on thoughts. I tend to not edit them, and try to get them right in one go. This is kinda the point of a thought, its a quick post meant to be the size of a tweet, but sometimes I'm leaving thoughts on a video or long post and want to make sure I have a good save point, but I just keep the thing in draft and hope I don't loose if for far too long.
Results
Let's see this change in action!!
before
This is the tiny ass form nested deeply in the flow of the feed. When I made it I naively just swapped out the post itself with the edit form, and swapped the post back in after edit.
thoughts is built with HTMX btw so all html is rendered in the backend and swapped by htmx client side.
after
Now the edit is a full page modal with a nice blurry backdrop effect to the
rest of the content. This feels pretty similar to making a new post
on
twitter.
How
How did I do this with htmx? I had to break out of the mindset my brain was in with swapping in place and letting the edit form take over the entire screen.
First the empty #modal-container
was added to the top of every page.
<div id="modal-container"></div>
Then each post that gets added to the page already had an edit button, but now
the target is set to #modal-container
, and the swap is set to innerHTML
so
that we keep the #modal-container
in place.
<button class="h-8 w-8 p-1 text-center" hx-get="{{ config.root }}/edit-thought/{{ post.id }}" hx-target="#modal-container" hx-swap="innerHTML" title="Edit"> {% include 'edit.svg' %} </button>
Now the edit post that is returned from the server is turned into a full height and width modal with a nice backgrop blur over the content.
<div id="modal-container"> <div class="fixed inset-0 z-50 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true"> <!-- Background backdrop --> <div class="fixed inset-0 bg-black/30 backdrop-blur-sm"></div> <!-- Modal panel --> <div class="flex min-h-screen items-center justify-center p-4"> <div class="relative w-full max-w-4xl transform rounded-xl bg-zinc-900 p-6 shadow-2xl transition-all"> ... similar to the original edit form in here </div> </div> </div> </div>
Clearing the modal
One notable change to the original edit form is clearing the modal container
on submit. It is done with an hx-on::after-request
event and one line of js.
<form id="websiteForm" hx-patch="{{ config.root }}/post/html/" method="POST" name="newPost" hx-target="#post-{{ post.id }}" hx-swap="outerHTML" hx-on::after-request="document.getElementById('modal-container').innerHTML = ''"> ``` Similarly on the Cancel button. ``` html <button class="rounded-lg border border-black bg-zinc-950 px-6 py-3 text-lg hover:bg-zinc-900" type="button" onclick="document.getElementById('modal-container').innerHTML = ''"> Cancel </button>
Hotkeys
Now I struggled to get this right with htmx, and it seemed like things were getting more complicated by trying to get the buttons to trigger using htmx triggered keyboard events, I ended up just using javascript. Its still in the same file, so locality of behavior is barely an issue on this one anyways, and the js was just working.
<script> document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { document.getElementById('modal-container').innerHTML = ''; } if (e.key === 'Enter' && e.ctrlKey) { e.preventDefault(); htmx.trigger('#websiteForm', 'submit'); } }); </script>
Other small change
When the server is returns an empty list of posts the post container would change size and cause a layout shift. Now the empty posts element, while not great does not cause layout shift.
<ul id="posts" class="min-h-screen"><li>No posts</li></ul>