New hierarchy: Geography (Reactive Islands, Hypermedia Lakes, Marshes, Isomorphism), Language (Docs, Specs, Bootstrappers, Testing), Applications (CSSX, Protocols), Etc (Essays, Philosophy, Plans). All routes updated to match: /reactive/* → /geography/reactive/*, /docs/* → /language/docs/*, /essays/* → /etc/essays/*, etc. Updates nav-data.sx, all defpage routes, API endpoints, internal links across 43 files. Enhanced find-nav-match for nested group resolution. Also includes: page-helpers-demo sf-total fix (reduce instead of set!), rebootstrapped sx-browser.js and sx_ref.py, defensive slice/rest guards. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
917 lines
43 KiB
Plaintext
917 lines
43 KiB
Plaintext
;; SX reference — demo components for attribute detail pages
|
|
;;
|
|
;; Each attribute gets a small, focused demo showing exactly
|
|
;; what that attribute does.
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-get
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-get-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-get-result"
|
|
:sx-swap "innerHTML"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Load server time")
|
|
(div :id "ref-get-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click to load.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-post
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-post-demo ()
|
|
(div :class "space-y-3"
|
|
(form
|
|
:sx-post "/geography/hypermedia/reference/api/greet"
|
|
:sx-target "#ref-post-result"
|
|
:sx-swap "innerHTML"
|
|
:class "flex gap-2"
|
|
(input :type "text" :name "name" :placeholder "Your name"
|
|
:class "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")
|
|
(button :type "submit"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Greet"))
|
|
(div :id "ref-post-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Submit to see greeting.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-put
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-put-demo ()
|
|
(div :id "ref-put-view"
|
|
(div :class "flex items-center justify-between p-3 bg-stone-100 rounded"
|
|
(span :class "text-stone-700 text-sm" "Status: " (strong "draft"))
|
|
(button
|
|
:sx-put "/geography/hypermedia/reference/api/status"
|
|
:sx-target "#ref-put-view"
|
|
:sx-swap "innerHTML"
|
|
:sx-vals "{\"status\": \"published\"}"
|
|
:class "px-3 py-1 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
|
"Publish"))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-delete
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-delete-demo ()
|
|
(div :class "space-y-2"
|
|
(div :id "ref-del-1" :class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
|
(span :class "text-sm text-stone-700" "Item A")
|
|
(button :sx-delete "/geography/hypermedia/reference/api/item/1"
|
|
:sx-target "#ref-del-1" :sx-swap "delete"
|
|
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
|
(div :id "ref-del-2" :class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
|
(span :class "text-sm text-stone-700" "Item B")
|
|
(button :sx-delete "/geography/hypermedia/reference/api/item/2"
|
|
:sx-target "#ref-del-2" :sx-swap "delete"
|
|
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
|
(div :id "ref-del-3" :class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
|
(span :class "text-sm text-stone-700" "Item C")
|
|
(button :sx-delete "/geography/hypermedia/reference/api/item/3"
|
|
:sx-target "#ref-del-3" :sx-swap "delete"
|
|
:class "text-red-500 text-sm hover:text-red-700" "Remove"))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-patch
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-patch-demo ()
|
|
(div :id "ref-patch-view" :class "space-y-2"
|
|
(div :class "p-3 bg-stone-100 rounded"
|
|
(span :class "text-stone-700 text-sm" "Theme: " (strong :id "ref-patch-val" "light")))
|
|
(div :class "flex gap-2"
|
|
(button :sx-patch "/geography/hypermedia/reference/api/theme"
|
|
:sx-vals "{\"theme\": \"dark\"}"
|
|
:sx-target "#ref-patch-val" :sx-swap "innerHTML"
|
|
:class "px-3 py-1 bg-stone-800 text-white rounded text-sm" "Dark")
|
|
(button :sx-patch "/geography/hypermedia/reference/api/theme"
|
|
:sx-vals "{\"theme\": \"light\"}"
|
|
:sx-target "#ref-patch-val" :sx-swap "innerHTML"
|
|
:class "px-3 py-1 bg-stone-100 border border-stone-300 text-stone-700 rounded text-sm" "Light"))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-trigger
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-trigger-demo ()
|
|
(div :class "space-y-3"
|
|
(input :type "text" :name "q" :placeholder "Type to search..."
|
|
:sx-get "/geography/hypermedia/reference/api/trigger-search"
|
|
:sx-trigger "input changed delay:300ms"
|
|
:sx-target "#ref-trigger-result"
|
|
:sx-swap "innerHTML"
|
|
:class "w-full px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")
|
|
(div :id "ref-trigger-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Start typing to trigger a search.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-target
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-target-demo ()
|
|
(div :class "space-y-3"
|
|
(div :class "flex gap-2"
|
|
(button :sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-target-a"
|
|
:sx-swap "innerHTML"
|
|
:class "px-3 py-1 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
|
"Update Box A")
|
|
(button :sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-target-b"
|
|
:sx-swap "innerHTML"
|
|
:class "px-3 py-1 bg-emerald-600 text-white rounded text-sm hover:bg-emerald-700"
|
|
"Update Box B"))
|
|
(div :class "grid grid-cols-2 gap-3"
|
|
(div :id "ref-target-a" :class "p-3 rounded border border-violet-200 bg-violet-50 text-sm text-stone-500"
|
|
"Box A")
|
|
(div :id "ref-target-b" :class "p-3 rounded border border-emerald-200 bg-emerald-50 text-sm text-stone-500"
|
|
"Box B"))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-swap
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-swap-demo ()
|
|
(div :class "space-y-3"
|
|
(div :class "flex gap-2 flex-wrap"
|
|
(button :sx-get "/geography/hypermedia/reference/api/swap-item"
|
|
:sx-target "#ref-swap-list" :sx-swap "beforeend"
|
|
:class "px-3 py-1 bg-violet-600 text-white rounded text-sm" "beforeend")
|
|
(button :sx-get "/geography/hypermedia/reference/api/swap-item"
|
|
:sx-target "#ref-swap-list" :sx-swap "afterbegin"
|
|
:class "px-3 py-1 bg-emerald-600 text-white rounded text-sm" "afterbegin")
|
|
(button :sx-get "/geography/hypermedia/reference/api/swap-item"
|
|
:sx-target "#ref-swap-list" :sx-swap "innerHTML"
|
|
:class "px-3 py-1 bg-blue-600 text-white rounded text-sm" "innerHTML"))
|
|
(div :id "ref-swap-list"
|
|
:class "p-3 rounded border border-stone-200 space-y-1 min-h-[3rem]"
|
|
(div :class "text-sm text-stone-500" "Original item"))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-swap-oob
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-oob-demo ()
|
|
(div :class "space-y-3"
|
|
(button :sx-get "/geography/hypermedia/reference/api/oob"
|
|
:sx-target "#ref-oob-main"
|
|
:sx-swap "innerHTML"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
|
"Update both boxes")
|
|
(div :class "grid grid-cols-2 gap-3"
|
|
(div :class "rounded border border-stone-200 p-3"
|
|
(div :class "text-xs text-stone-400 mb-1" "Main target")
|
|
(div :id "ref-oob-main" :class "text-sm text-stone-500" "Waiting..."))
|
|
(div :class "rounded border border-stone-200 p-3"
|
|
(div :class "text-xs text-stone-400 mb-1" "OOB target")
|
|
(div :id "ref-oob-side" :class "text-sm text-stone-500" "Waiting...")))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-select
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-select-demo ()
|
|
(div :class "space-y-3"
|
|
(button :sx-get "/geography/hypermedia/reference/api/select-page"
|
|
:sx-target "#ref-select-result"
|
|
:sx-select "#the-content"
|
|
:sx-swap "innerHTML"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
|
"Load (selecting #the-content)")
|
|
(div :id "ref-select-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Only the selected fragment will appear here.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-confirm
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-confirm-demo ()
|
|
(div :class "space-y-2"
|
|
(div :id "ref-confirm-item"
|
|
:class "flex items-center justify-between p-3 border border-stone-200 rounded"
|
|
(span :class "text-sm text-stone-700" "Important file.txt")
|
|
(button :sx-delete "/geography/hypermedia/reference/api/item/confirm"
|
|
:sx-target "#ref-confirm-item" :sx-swap "delete"
|
|
:sx-confirm "Are you sure you want to delete this file?"
|
|
:class "px-3 py-1 text-red-500 text-sm border border-red-200 rounded hover:bg-red-50"
|
|
"Delete"))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-push-url
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-pushurl-demo ()
|
|
(div :class "space-y-3"
|
|
(div :class "flex gap-2"
|
|
(a :href "/geography/hypermedia/reference/attributes/sx-get"
|
|
:sx-get "/geography/hypermedia/reference/attributes/sx-get"
|
|
:sx-target "#main-panel" :sx-select "#main-panel"
|
|
:sx-swap "outerHTML" :sx-push-url "true"
|
|
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
|
"sx-get page")
|
|
(a :href "/geography/hypermedia/reference/attributes/sx-post"
|
|
:sx-get "/geography/hypermedia/reference/attributes/sx-post"
|
|
:sx-target "#main-panel" :sx-select "#main-panel"
|
|
:sx-swap "outerHTML" :sx-push-url "true"
|
|
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
|
"sx-post page"))
|
|
(p :class "text-sm text-stone-500"
|
|
"Click a link — the URL bar updates without a full page reload. Use browser back to return.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-sync
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-sync-demo ()
|
|
(div :class "space-y-3"
|
|
(input :type "text" :name "q" :placeholder "Type quickly..."
|
|
:sx-get "/geography/hypermedia/reference/api/slow-echo"
|
|
:sx-trigger "input changed delay:100ms"
|
|
:sx-sync "replace"
|
|
:sx-target "#ref-sync-result"
|
|
:sx-swap "innerHTML"
|
|
:class "w-full px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")
|
|
(p :class "text-xs text-stone-400"
|
|
"With sync:replace, each new keystroke aborts the in-flight request.")
|
|
(div :id "ref-sync-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Type to see only the latest result.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-encoding
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-encoding-demo ()
|
|
(div :class "space-y-3"
|
|
(form :sx-post "/geography/hypermedia/reference/api/upload-name"
|
|
:sx-encoding "multipart/form-data"
|
|
:sx-target "#ref-encoding-result"
|
|
:sx-swap "innerHTML"
|
|
:class "flex gap-2"
|
|
(input :type "file" :name "file"
|
|
:class "flex-1 text-sm text-stone-500 file:mr-2 file:px-3 file:py-1 file:rounded file:border-0 file:text-sm file:bg-violet-50 file:text-violet-700")
|
|
(button :type "submit"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
|
"Upload"))
|
|
(div :id "ref-encoding-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Select a file and submit.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-headers
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-headers-demo ()
|
|
(div :class "space-y-3"
|
|
(button :sx-get "/geography/hypermedia/reference/api/echo-headers"
|
|
:sx-headers {:X-Custom-Token "abc123" :X-Request-Source "demo"}
|
|
:sx-target "#ref-headers-result"
|
|
:sx-swap "innerHTML"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
|
"Send with custom headers")
|
|
(div :id "ref-headers-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click to see echoed headers.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-include
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-include-demo ()
|
|
(div :class "space-y-3"
|
|
(div :class "flex gap-2 items-end"
|
|
(div
|
|
(label :class "block text-xs text-stone-500 mb-1" "Category")
|
|
(select :id "ref-inc-cat" :name "category"
|
|
:class "px-3 py-2 border border-stone-300 rounded text-sm"
|
|
(option :value "all" "All")
|
|
(option :value "books" "Books")
|
|
(option :value "tools" "Tools")))
|
|
(button :sx-get "/geography/hypermedia/reference/api/echo-vals"
|
|
:sx-include "#ref-inc-cat"
|
|
:sx-target "#ref-include-result"
|
|
:sx-swap "innerHTML"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
|
"Filter"))
|
|
(div :id "ref-include-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click Filter — the select value is included in the request.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-vals
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-vals-demo ()
|
|
(div :class "space-y-3"
|
|
(button :sx-post "/geography/hypermedia/reference/api/echo-vals"
|
|
:sx-vals "{\"source\": \"demo\", \"page\": \"3\"}"
|
|
:sx-target "#ref-vals-result"
|
|
:sx-swap "innerHTML"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
|
"Send with extra values")
|
|
(div :id "ref-vals-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click to see echoed values.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-media
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-media-demo ()
|
|
(div :class "space-y-3"
|
|
(a :href "/geography/hypermedia/reference/attributes/sx-get"
|
|
:sx-get "/geography/hypermedia/reference/attributes/sx-get"
|
|
:sx-target "#main-panel" :sx-select "#main-panel"
|
|
:sx-swap "outerHTML" :sx-push-url "true"
|
|
:sx-media "(min-width: 768px)"
|
|
:class "inline-block px-4 py-2 bg-violet-600 text-white rounded text-sm no-underline hover:bg-violet-700"
|
|
"sx navigation (desktop only)")
|
|
(p :class "text-sm text-stone-500"
|
|
"On screens narrower than 768px this link uses normal navigation. On wider screens it uses sx.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-disable
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-disable-demo ()
|
|
(div :class "space-y-3"
|
|
(div :class "grid grid-cols-2 gap-3"
|
|
(div :class "p-3 border border-stone-200 rounded"
|
|
(p :class "text-xs text-stone-400 mb-2" "sx enabled")
|
|
(button :sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-dis-a" :sx-swap "innerHTML"
|
|
:class "px-3 py-1 bg-violet-600 text-white rounded text-sm" "Load")
|
|
(div :id "ref-dis-a" :class "mt-2 text-sm text-stone-500" "—"))
|
|
(div :sx-disable "true" :class "p-3 border border-stone-200 rounded"
|
|
(p :class "text-xs text-stone-400 mb-2" "sx disabled")
|
|
(button :sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-dis-b" :sx-swap "innerHTML"
|
|
:class "px-3 py-1 bg-stone-400 text-white rounded text-sm" "Load")
|
|
(div :id "ref-dis-b" :class "mt-2 text-sm text-stone-500"
|
|
"Button won't fire sx request")))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-on:*
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-on-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-on:click "document.getElementById('ref-on-result').textContent = 'Clicked at ' + new Date().toLocaleTimeString()"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
|
"Click me")
|
|
(div :id "ref-on-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click the button — runs JavaScript, no server request.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-retry
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-retry-demo ()
|
|
(div :class "space-y-3"
|
|
(button :sx-get "/geography/hypermedia/reference/api/flaky"
|
|
:sx-target "#ref-retry-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-retry "true"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
|
"Call flaky endpoint")
|
|
(div :id "ref-retry-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"This endpoint fails 2 out of 3 times. sx-retry retries automatically.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; data-sx
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-data-sx-demo ()
|
|
(div :class "space-y-3"
|
|
(div :data-sx "(div :class \"p-3 bg-violet-50 rounded\" (h3 :class \"font-semibold text-violet-800\" \"Client-rendered\") (p :class \"text-sm text-stone-600\" \"This was evaluated in the browser — no server request.\"))")
|
|
(p :class "text-xs text-stone-400" "The content above is rendered client-side from the data-sx attribute.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; data-sx-env
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-data-sx-env-demo ()
|
|
(div :class "space-y-3"
|
|
(div :data-sx "(div :class \"p-3 bg-emerald-50 rounded\" (h3 :class \"font-semibold text-emerald-800\" title) (p :class \"text-sm text-stone-600\" message))"
|
|
:data-sx-env "{\"title\": \"Dynamic content\", \"message\": \"Variables passed via data-sx-env are available in the expression.\"}")
|
|
(p :class "text-xs text-stone-400" "The title and message above come from the data-sx-env JSON.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-boost
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-boost-demo ()
|
|
(div :class "space-y-3"
|
|
(nav :sx-boost "true" :class "flex gap-3"
|
|
(a :href "/geography/hypermedia/reference/attributes/sx-get"
|
|
:class "text-violet-600 hover:text-violet-800 underline text-sm"
|
|
"sx-get")
|
|
(a :href "/geography/hypermedia/reference/attributes/sx-post"
|
|
:class "text-violet-600 hover:text-violet-800 underline text-sm"
|
|
"sx-post")
|
|
(a :href "/geography/hypermedia/reference/attributes/sx-target"
|
|
:class "text-violet-600 hover:text-violet-800 underline text-sm"
|
|
"sx-target"))
|
|
(p :class "text-xs text-stone-400"
|
|
"These links use AJAX navigation via sx-boost — no sx-get needed on each link. "
|
|
"Set the value to a CSS selector (e.g. sx-boost=\"#main-panel\") to configure the default swap target for all descendants.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-preload
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-preload-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-preload-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-preload "mouseover"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Hover then click (preloaded)")
|
|
(div :id "ref-preload-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Hover over the button first, then click — the response is instant.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-preserve
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-preserve-demo ()
|
|
(div :class "space-y-3"
|
|
(div :class "flex gap-2 items-center"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-preserve-container"
|
|
:sx-swap "innerHTML"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Swap container")
|
|
(span :class "text-xs text-stone-400" "The input below keeps its value across swaps."))
|
|
(div :id "ref-preserve-container" :class "space-y-2"
|
|
(input :id "ref-preserved-input" :sx-preserve "true"
|
|
:type "text" :placeholder "Type here — preserved across swaps"
|
|
:class "w-full px-3 py-2 border border-stone-300 rounded text-sm")
|
|
(div :class "p-2 bg-stone-100 rounded text-sm text-stone-600"
|
|
"This text will be replaced on swap."))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-indicator
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-indicator-demo ()
|
|
(div :class "space-y-3"
|
|
(div :class "flex gap-3 items-center"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/slow-echo"
|
|
:sx-target "#ref-indicator-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-indicator "#ref-spinner"
|
|
:sx-vals "{\"q\": \"hello\"}"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Load (slow)")
|
|
(span :id "ref-spinner"
|
|
:class "text-violet-600 text-sm"
|
|
:style "display: none"
|
|
"Loading..."))
|
|
(div :id "ref-indicator-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click to load (indicator shows during request).")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-validate
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-validate-demo ()
|
|
(div :class "space-y-3"
|
|
(form
|
|
:sx-post "/geography/hypermedia/reference/api/greet"
|
|
:sx-target "#ref-validate-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-validate "true"
|
|
:class "flex gap-2"
|
|
(input :type "email" :name "name" :required "true"
|
|
:placeholder "Enter email (required)"
|
|
:class "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")
|
|
(button :type "submit"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Submit"))
|
|
(div :id "ref-validate-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Submit with invalid/empty email to see validation.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-ignore
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-ignore-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-ignore-container"
|
|
:sx-swap "innerHTML"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Swap container")
|
|
(div :id "ref-ignore-container" :class "space-y-2"
|
|
(div :sx-ignore "true" :class "p-2 bg-amber-50 rounded border border-amber-200"
|
|
(p :class "text-sm text-amber-800" "This subtree has sx-ignore — it won't change.")
|
|
(input :type "text" :placeholder "Type here — ignored during swap"
|
|
:class "mt-1 w-full px-2 py-1 border border-amber-300 rounded text-sm"))
|
|
(div :class "p-2 bg-stone-100 rounded text-sm text-stone-600"
|
|
"This text WILL be replaced on swap."))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-optimistic
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-optimistic-demo ()
|
|
(div :class "space-y-2"
|
|
(div :id "ref-opt-item-1"
|
|
:class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
|
(span :class "text-sm text-stone-700" "Optimistic item A")
|
|
(button :sx-delete "/geography/hypermedia/reference/api/item/opt1"
|
|
:sx-target "#ref-opt-item-1" :sx-swap "delete"
|
|
:sx-optimistic "remove"
|
|
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
|
(div :id "ref-opt-item-2"
|
|
:class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
|
(span :class "text-sm text-stone-700" "Optimistic item B")
|
|
(button :sx-delete "/geography/hypermedia/reference/api/item/opt2"
|
|
:sx-target "#ref-opt-item-2" :sx-swap "delete"
|
|
:sx-optimistic "remove"
|
|
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
|
(p :class "text-xs text-stone-400"
|
|
"Items fade out immediately on click (optimistic), then are removed when the server responds.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-replace-url
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-replace-url-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-replurl-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-replace-url "true"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Load (replaces URL)")
|
|
(div :id "ref-replurl-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click to load — URL changes but no new history entry.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-disabled-elt
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-disabled-elt-demo ()
|
|
(div :class "space-y-3"
|
|
(div :class "flex gap-3 items-center"
|
|
(button :id "ref-diselt-btn"
|
|
:sx-get "/geography/hypermedia/reference/api/slow-echo"
|
|
:sx-target "#ref-diselt-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-disabled-elt "#ref-diselt-btn"
|
|
:sx-vals "{\"q\": \"hello\"}"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm disabled:opacity-50"
|
|
"Click (disables during request)")
|
|
(span :class "text-xs text-stone-400" "Button is disabled while request is in-flight."))
|
|
(div :id "ref-diselt-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click the button to see it disable during the request.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-prompt
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-prompt-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/prompt-echo"
|
|
:sx-target "#ref-prompt-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-prompt "Enter your name:"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Prompt & send")
|
|
(div :id "ref-prompt-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click to enter a name via prompt — it is sent as the SX-Prompt header.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-params
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-params-demo ()
|
|
(div :class "space-y-3"
|
|
(form
|
|
:sx-post "/geography/hypermedia/reference/api/echo-vals"
|
|
:sx-target "#ref-params-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-params "name"
|
|
:class "flex gap-2"
|
|
(input :type "text" :name "name" :placeholder "Name (sent)"
|
|
:class "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")
|
|
(input :type "text" :name "secret" :placeholder "Secret (filtered)"
|
|
:class "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")
|
|
(button :type "submit"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Submit"))
|
|
(div :id "ref-params-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Only 'name' will be sent — 'secret' is filtered by sx-params.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx-sse
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-sse-demo ()
|
|
(div :class "space-y-3"
|
|
(div :sx-sse "/geography/hypermedia/reference/api/sse-time"
|
|
:sx-sse-swap "time"
|
|
:sx-swap "innerHTML"
|
|
(div :id "ref-sse-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-600 text-sm font-mono"
|
|
"Connecting to SSE stream..."))
|
|
(p :class "text-xs text-stone-400"
|
|
"Server pushes time updates every 2 seconds via Server-Sent Events.")))
|
|
|
|
;; ===========================================================================
|
|
;; Header detail demos
|
|
;; ===========================================================================
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; SX-Prompt header demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-header-prompt-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/prompt-echo"
|
|
:sx-target "#ref-hdr-prompt-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-prompt "Enter your name:"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Prompt & send")
|
|
(div :id "ref-hdr-prompt-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click to enter a name via prompt — the value is sent as the SX-Prompt header.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; SX-Trigger response header demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-header-trigger-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/trigger-event"
|
|
:sx-target "#ref-hdr-trigger-result"
|
|
:sx-swap "innerHTML"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Load with trigger")
|
|
(div :id "ref-hdr-trigger-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
:sx-on:showNotice "this.style.borderColor = '#8b5cf6'; this.style.borderWidth = '2px'"
|
|
"Click — the server response includes SX-Trigger: showNotice, which highlights this box.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; SX-Retarget response header demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-header-retarget-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/retarget"
|
|
:sx-target "#ref-hdr-retarget-main"
|
|
:sx-swap "innerHTML"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Load (server retargets)")
|
|
(div :class "grid grid-cols-2 gap-3"
|
|
(div :class "rounded border border-stone-200 p-3"
|
|
(div :class "text-xs text-stone-400 mb-1" "Original target")
|
|
(div :id "ref-hdr-retarget-main" :class "text-sm text-stone-500" "Waiting..."))
|
|
(div :class "rounded border border-stone-200 p-3"
|
|
(div :class "text-xs text-stone-400 mb-1" "Retarget destination")
|
|
(div :id "ref-hdr-retarget-alt" :class "text-sm text-stone-500" "Waiting...")))))
|
|
|
|
;; ===========================================================================
|
|
;; Event detail demos
|
|
;; ===========================================================================
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx:beforeRequest event demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-event-before-request-demo ()
|
|
(div :class "space-y-3"
|
|
(div :class "flex gap-2 items-center"
|
|
(input :id "ref-evt-br-input" :type "text" :placeholder "Type something first..."
|
|
:class "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-evt-br-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-on:sx:beforeRequest "if (!document.getElementById('ref-evt-br-input').value) { event.preventDefault(); document.getElementById('ref-evt-br-result').textContent = 'Cancelled — input is empty!'; }"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Load"))
|
|
(div :id "ref-evt-br-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Request is cancelled via preventDefault() if the input is empty.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx:afterRequest event demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-event-after-request-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/time"
|
|
:sx-target "#ref-evt-ar-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-on:sx:afterRequest "document.getElementById('ref-evt-ar-log').textContent = 'Response status: ' + (event.detail ? event.detail.status : '?')"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Load (logs after response)")
|
|
(div :id "ref-evt-ar-log"
|
|
:class "p-2 rounded bg-emerald-50 text-emerald-700 text-sm"
|
|
"Event log will appear here.")
|
|
(div :id "ref-evt-ar-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click to load — afterRequest fires before the swap.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx:afterSwap event demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-event-after-swap-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/swap-item"
|
|
:sx-target "#ref-evt-as-list"
|
|
:sx-swap "beforeend"
|
|
:sx-on:sx:afterSwap "var items = document.querySelectorAll('#ref-evt-as-list > div'); if (items.length) items[items.length-1].scrollIntoView({behavior:'smooth'}); document.getElementById('ref-evt-as-count').textContent = items.length + ' items'"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Add item (scrolls after swap)")
|
|
(div :id "ref-evt-as-count"
|
|
:class "text-sm text-emerald-700"
|
|
"1 items")
|
|
(div :id "ref-evt-as-list"
|
|
:class "p-3 rounded border border-stone-200 space-y-1 max-h-32 overflow-y-auto"
|
|
(div :class "text-sm text-stone-500" "Items will be appended and scrolled into view."))))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx:responseError event demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-event-response-error-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "/geography/hypermedia/reference/api/error-500"
|
|
:sx-target "#ref-evt-err-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-on:sx:responseError "var s=document.getElementById('ref-evt-err-status'); s.style.display='block'; s.textContent='Error ' + (event.detail ? event.detail.status || '?' : '?') + ' received'"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Call failing endpoint")
|
|
(div :id "ref-evt-err-status"
|
|
:class "p-2 rounded bg-red-50 text-red-600 text-sm"
|
|
:style "display: none"
|
|
"")
|
|
(div :id "ref-evt-err-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click to trigger an error — the sx:responseError event fires.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx:validationFailed event demo
|
|
;; ---------------------------------------------------------------------------
|
|
;; @css invalid:border-red-400
|
|
|
|
(defcomp ~ref-event-validation-failed-demo ()
|
|
(div :class "space-y-3"
|
|
(form
|
|
:sx-post "/geography/hypermedia/reference/api/greet"
|
|
:sx-target "#ref-evt-vf-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-validate "true"
|
|
:sx-on:sx:validationFailed "document.getElementById('ref-evt-vf-status').style.display = 'block'"
|
|
:class "flex gap-2"
|
|
(input :type "email" :name "email" :required "true"
|
|
:placeholder "Email (required)"
|
|
:class "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500 invalid:border-red-400")
|
|
(button :type "submit"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Submit"))
|
|
(div :id "ref-evt-vf-status"
|
|
:class "p-2 rounded bg-amber-50 text-amber-700 text-sm"
|
|
:style "display: none"
|
|
"Validation failed — form was not submitted.")
|
|
(div :id "ref-evt-vf-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Submit with empty/invalid email to trigger the event.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx:requestError event demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-event-request-error-demo ()
|
|
(div :class "space-y-3"
|
|
(button
|
|
:sx-get "https://this-domain-does-not-exist.invalid/api"
|
|
:sx-target "#ref-evt-re-result"
|
|
:sx-swap "innerHTML"
|
|
:sx-on:sx:requestError "document.getElementById('ref-evt-re-status').style.display = 'block'; document.getElementById('ref-evt-re-status').textContent = 'Network error — request never reached a server'"
|
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
|
"Request invalid domain")
|
|
(div :id "ref-evt-re-status"
|
|
:class "p-2 rounded bg-red-50 text-red-600 text-sm"
|
|
:style "display: none"
|
|
"")
|
|
(div :id "ref-evt-re-result"
|
|
:class "p-3 rounded bg-stone-100 text-stone-400 text-sm"
|
|
"Click to trigger a network error — sx:requestError fires.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx:clientRoute event demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-event-client-route-demo ()
|
|
(div :class "space-y-3"
|
|
(p :class "text-sm text-stone-600"
|
|
"Open DevTools console, then navigate to a pure page (no :data expression). "
|
|
"You'll see \"sx:route client /path\" in the console — no network request is made.")
|
|
(div :class "flex gap-2 flex-wrap"
|
|
(a :href "/etc/essays/"
|
|
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
|
"Essays")
|
|
(a :href "/etc/plans/"
|
|
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
|
"Plans")
|
|
(a :href "/applications/protocols/"
|
|
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
|
"Protocols"))
|
|
(p :class "text-xs text-stone-400"
|
|
"The sx:clientRoute event fires on the swap target and bubbles to document.body. "
|
|
"Apps use it to update nav selection, analytics, or other post-navigation state.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx:sseOpen event demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-event-sse-open-demo ()
|
|
(div :class "space-y-3"
|
|
(div :sx-sse "/geography/hypermedia/reference/api/sse-time"
|
|
:sx-sse-swap "time"
|
|
:sx-swap "innerHTML"
|
|
:sx-on:sx:sseOpen "document.getElementById('ref-evt-sseopen-status').textContent = 'Connected'; document.getElementById('ref-evt-sseopen-status').className = 'inline-block px-2 py-0.5 rounded text-xs bg-emerald-100 text-emerald-700'"
|
|
(div :class "flex items-center gap-3"
|
|
(span :id "ref-evt-sseopen-status"
|
|
:class "inline-block px-2 py-0.5 rounded text-xs bg-amber-100 text-amber-700"
|
|
"Connecting...")
|
|
(span :class "text-sm text-stone-500" "SSE stream")))
|
|
(p :class "text-xs text-stone-400"
|
|
"The status badge turns green when the SSE connection opens.")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx:sseMessage event demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-event-sse-message-demo ()
|
|
(div :class "space-y-3"
|
|
(div :sx-sse "/geography/hypermedia/reference/api/sse-time"
|
|
:sx-sse-swap "time"
|
|
:sx-swap "innerHTML"
|
|
:sx-on:sx:sseMessage "var c = parseInt(document.getElementById('ref-evt-ssemsg-count').dataset.count || '0') + 1; document.getElementById('ref-evt-ssemsg-count').dataset.count = c; document.getElementById('ref-evt-ssemsg-count').textContent = c + ' messages received'"
|
|
(div :id "ref-evt-ssemsg-output"
|
|
:class "p-3 rounded bg-stone-100 text-stone-600 text-sm font-mono"
|
|
"Waiting for SSE messages..."))
|
|
(div :id "ref-evt-ssemsg-count"
|
|
:class "text-sm text-emerald-700"
|
|
:data-count "0"
|
|
"0 messages received")))
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
;; sx:sseError event demo
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
(defcomp ~ref-event-sse-error-demo ()
|
|
(div :class "space-y-3"
|
|
(div :sx-sse "/geography/hypermedia/reference/api/sse-time"
|
|
:sx-sse-swap "time"
|
|
:sx-swap "innerHTML"
|
|
:sx-on:sx:sseError "document.getElementById('ref-evt-sseerr-status').textContent = 'Disconnected'; document.getElementById('ref-evt-sseerr-status').className = 'inline-block px-2 py-0.5 rounded text-xs bg-red-100 text-red-700'"
|
|
:sx-on:sx:sseOpen "document.getElementById('ref-evt-sseerr-status').textContent = 'Connected'; document.getElementById('ref-evt-sseerr-status').className = 'inline-block px-2 py-0.5 rounded text-xs bg-emerald-100 text-emerald-700'"
|
|
(div :class "flex items-center gap-3"
|
|
(span :id "ref-evt-sseerr-status"
|
|
:class "inline-block px-2 py-0.5 rounded text-xs bg-amber-100 text-amber-700"
|
|
"Connecting...")
|
|
(span :class "text-sm text-stone-500" "SSE stream")))
|
|
(p :class "text-xs text-stone-400"
|
|
"If the SSE connection drops, the badge turns red via sx:sseError.")))
|