From e7b77bdb72eb53a60adeb28c44b2f81c989168b4 Mon Sep 17 00:00:00 2001
From: Akira Tempaku <paku@skyizwhite.dev>
Date: Sat, 3 May 2025 15:11:31 +0900
Subject: [PATCH] Preserve header state during page transitions

---
 src/components/header.lisp | 102 ++++++++++++++++++-------------------
 src/components/layout.lisp |   4 +-
 2 files changed, 54 insertions(+), 52 deletions(-)

diff --git a/src/components/header.lisp b/src/components/header.lisp
index 5fd82c3..97da0cd 100644
--- a/src/components/header.lisp
+++ b/src/components/header.lisp
@@ -13,65 +13,65 @@
 
 (defcomp ~pc-header ()
   (hsx
-   (nav :class "hidden md:flex items-end"
-     (ul :preload "mouseover" :class "flex gap-4 text-xl font-medium"
-       (loop
-         :for (href label) :in *nav-menu* :collect
-            (if (search href (request-uri jingle:*request*))
-                (hsx (li :class "text-pink-500"
-                       label))
-                (hsx (li (a :href href :class "hover:text-pink-500"
-                           label)))))))))
-
-(defcomp ~sp-header ()
-  (hsx
-   (div :class "block md:hidden" :x-data "{ open: false }"
-     (button
-       :class "z-20 size-8 flex flex-col justify-center cursor-pointer relative"
-       :type "button"
-       :@click "open = !open"
-       (div :class "grid justify-items-center gap-1.5"
-         (span
-           :class "h-1 w-8 rounded-full bg-black transition duration-400"
-           :|:class| "open && 'rotate-45 translate-y-2.5'")
-         (span
-           :class "h-1 w-8 rounded-full bg-black transition duration-400"
-           :|:class| "open && 'scale-x-0'")
-         (span
-           :class "h-1 w-8 rounded-full bg-black transition duration-400"
-           :|:class| "open && '-rotate-45 -translate-y-2.5'")))
-     (nav
-       :class (<>
-                "fixed flex flex-col items-center justify-center "
-                "z-10 top-0 right-0 w-full h-full gap-16 bg-gray-200")
-       :x-show "open"
-       :|x-transition:enter| "transition ease-out duration-400"
-       :|x-transition:enter-start| "translate-x-full"
-       :|x-transition:enter-end| "translate-x-0"
-       :|x-transition:leave| "transition ease-in duration-400"
-       :|x-transition:leave-start| "translate-x-0"
-       :|x-transition:leave-end| "translate-x-full"
-       (h2 :class "text-5xl font-bold"
-         "Menu")
-       (ul 
-         :preload "mousedown"
-         :class "flex flex-col h-fit gap-8 text-3xl font-medium"
+   (header :class "hidden md:flex justify-between py-4 border-b-1 top-0 bg-white"
+     (h1 :class "z-20 text-3xl font-bold"
+       (a :href "/"
+         "skyizwhite"))
+     (nav :class "flex items-end"
+       (ul :preload "mouseover" :class "flex gap-4 text-xl font-medium"
          (loop
            :for (href label) :in *nav-menu* :collect
               (if (search href (request-uri jingle:*request*))
                   (hsx (li :class "text-pink-500"
                          label))
-                  (hsx (li (a 
-                             :href href
-                             :class "hover:text-pink-500"
-                             :@click "open = false"
+                  (hsx (li (a :href href :class "hover:text-pink-500"
                              label))))))))))
 
+(defcomp ~sp-header ()
+  (hsx
+   (header
+     :id "sp-header" :x-data "{ open: false }" :hx-preserve t
+     :class "flex md:hidden justify-between py-2 border-b-1 top-0 bg-white"     
+     (h1 :class "z-20 text-2xl font-bold"
+       (a :href "/" :@click "open = false"
+         "skyizwhite"))
+     (div
+       (button
+         :class "z-20 size-8 flex flex-col justify-center cursor-pointer relative"
+         :type "button"
+         :@click "open = !open"
+         (div :class "grid justify-items-center gap-1.5"
+           (span
+             :class "h-1 w-8 rounded-full bg-black transition duration-400"
+             :|:class| "open && 'rotate-45 translate-y-2.5'")
+           (span
+             :class "h-1 w-8 rounded-full bg-black transition duration-400"
+             :|:class| "open && 'scale-x-0'")
+           (span
+             :class "h-1 w-8 rounded-full bg-black transition duration-400"
+             :|:class| "open && '-rotate-45 -translate-y-2.5'")))
+       (nav
+         :class (<>
+                  "fixed flex flex-col items-center justify-center "
+                  "z-10 top-0 right-0 w-full h-full gap-16 bg-gray-200")
+         :x-show "open"
+         :|x-transition:enter| "transition ease-out duration-400"
+         :|x-transition:enter-start| "translate-x-full"
+         :|x-transition:enter-end| "translate-x-0"
+         :|x-transition:leave| "transition ease-in duration-400"
+         :|x-transition:leave-start| "translate-x-0"
+         :|x-transition:leave-end| "translate-x-full"
+         (h2 :class "text-5xl font-bold"
+           "Menu")
+         (ul 
+           :preload "mousedown"
+           :class "flex flex-col h-fit gap-8 text-3xl font-medium"
+           (loop
+             :for (href label) :in (append '(("/" "home")) *nav-menu*) :collect
+                (hsx (li (a :href href :@click "open = false" label))))))))))
+
 (defcomp ~header ()
   (hsx
-   (header :class "flex justify-between py-2 md:py-4 border-b-1 top-0 bg-white"
-     (h1 :class "z-20 text-2xl md:text-3xl font-bold"
-       (a :href "/"
-         "skyizwhite"))
+   (<>
      (~pc-header)
      (~sp-header))))
diff --git a/src/components/layout.lisp b/src/components/layout.lisp
index ae75116..3beb8fd 100644
--- a/src/components/layout.lisp
+++ b/src/components/layout.lisp
@@ -31,9 +31,11 @@
        (script :src "https://cdn.jsdelivr.net/npm/htmx-ext-preload@2.1.1/dist/preload.min.js")
        (script :src "https://cdn.jsdelivr.net/npm/htmx-ext-head-support@2.0.4/dist/head-support.min.js")
        (script :src "https://cdn.jsdelivr.net/npm/htmx-ext-response-targets@2.0.3/dist/response-targets.min.js")
+       (script :src "https://cdn.jsdelivr.net/npm/htmx-ext-alpine-morph@2.0.1/alpine-morph.min.js")
+       (script :src "https://cdn.jsdelivr.net/npm/@alpinejs/morph@3.14.9/dist/cdn.min.js" :defer t)
        (script :src "https://cdn.jsdelivr.net/npm/alpinejs@3.14.9/dist/cdn.min.js" :defer t))
      (body
-       :hx-ext "head-support, response-targets, preload"
+       :hx-ext "head-support, response-targets, preload, alpine-morph"
        :hx-boost "true" :hx-target-404 "body" :hx-target-5* "body"
        :class (<> 
                 "flex flex-col h-[100svh] w-full max-w-[700px] "