|
|
|
|
@@ -291,7 +291,7 @@
|
|
|
|
|
|
|
|
|
|
(~demo-marsh-product)
|
|
|
|
|
|
|
|
|
|
(~doc-code :code (highlight ";; Island with a store-backed price signal\n(defisland ~demo-marsh-product ()\n (let ((price (def-store \"demo-price\" (fn () (signal 19.99))))\n (qty (signal 1))\n (total (computed (fn () (* (deref price) (deref qty))))))\n (div\n ;; Reactive price display — updates when store changes\n (span \"$\" (deref price))\n (span \"Qty:\") (button \"-\") (span (deref qty)) (button \"+\")\n (span \"Total: $\" (deref total))\n\n ;; Fetch from server — response arrives as hypermedia\n (button :sx-get \"/(geography.(reactive.(api.flash-sale)))\"\n :sx-target \"#marsh-server-msg\"\n :sx-swap \"innerHTML\"\n \"Fetch Price\")\n ;; Server response lands here:\n (div :id \"marsh-server-msg\"))))" "lisp"))
|
|
|
|
|
(~doc-code :code (highlight ";; Island with a store-backed price signal\n(defisland ~demo-marsh-product ()\n (let ((price (def-store \"demo-price\" (fn () (signal 19.99))))\n (qty (signal 1))\n (total (computed (fn () (* (deref price) (deref qty))))))\n (div\n ;; Reactive price display — updates when store changes\n (span \"$\" (deref price))\n (span \"Qty:\") (button \"-\") (span (deref qty)) (button \"+\")\n (span \"Total: $\" (deref total))\n\n ;; Fetch from server — response arrives as hypermedia\n (button :sx-get \"/sx/(geography.(reactive.(api.flash-sale)))\"\n :sx-target \"#marsh-server-msg\"\n :sx-swap \"innerHTML\"\n \"Fetch Price\")\n ;; Server response lands here:\n (div :id \"marsh-server-msg\"))))" "lisp"))
|
|
|
|
|
|
|
|
|
|
(~doc-code :code (highlight ";; Server returns SX content + a data-init script:\n;;\n;; (<>\n;; (p \"Flash sale! Price: $14.99\")\n;; (script :type \"text/sx\" :data-init\n;; \"(reset! (use-store \\\"demo-price\\\") 14.99)\"))\n;;\n;; The <p> is swapped in as normal hypermedia content.\n;; The script writes to the store signal.\n;; The island's (deref price), total, and savings\n;; all update reactively — no re-render, no diffing." "lisp"))
|
|
|
|
|
|
|
|
|
|
@@ -315,7 +315,7 @@
|
|
|
|
|
(div :id "marsh-flash-target"
|
|
|
|
|
:class "min-h-[2rem]")
|
|
|
|
|
(button :class "mt-2 px-3 py-1.5 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
|
|
|
|
:sx-get "/(geography.(reactive.(api.flash-sale)))"
|
|
|
|
|
:sx-get "/sx/(geography.(reactive.(api.flash-sale)))"
|
|
|
|
|
:sx-target "#marsh-flash-target"
|
|
|
|
|
:sx-swap "innerHTML"
|
|
|
|
|
"Fetch from server"))
|
|
|
|
|
@@ -334,7 +334,7 @@
|
|
|
|
|
|
|
|
|
|
(~demo-marsh-settle)
|
|
|
|
|
|
|
|
|
|
(~doc-code :code (highlight ";; sx-on-settle runs SX after the swap settles\n(defisland ~demo-marsh-settle ()\n (let ((count (def-store \"settle-count\" (fn () (signal 0)))))\n (div\n ;; Reactive counter — updates from sx-on-settle\n (span \"Fetched: \" (deref count) \" times\")\n\n ;; Button with sx-on-settle hook\n (button :sx-get \"/(geography.(reactive.(api.settle-data)))\"\n :sx-target \"#settle-result\"\n :sx-swap \"innerHTML\"\n :sx-on-settle \"(swap! (use-store \\\"settle-count\\\") inc)\"\n \"Fetch Item\")\n\n ;; Server content lands here (pure hypermedia)\n (div :id \"settle-result\"))))" "lisp"))
|
|
|
|
|
(~doc-code :code (highlight ";; sx-on-settle runs SX after the swap settles\n(defisland ~demo-marsh-settle ()\n (let ((count (def-store \"settle-count\" (fn () (signal 0)))))\n (div\n ;; Reactive counter — updates from sx-on-settle\n (span \"Fetched: \" (deref count) \" times\")\n\n ;; Button with sx-on-settle hook\n (button :sx-get \"/sx/(geography.(reactive.(api.settle-data)))\"\n :sx-target \"#settle-result\"\n :sx-swap \"innerHTML\"\n :sx-on-settle \"(swap! (use-store \\\"settle-count\\\") inc)\"\n \"Fetch Item\")\n\n ;; Server content lands here (pure hypermedia)\n (div :id \"settle-result\"))))" "lisp"))
|
|
|
|
|
|
|
|
|
|
(p "The server knows nothing about signals or counters. It returns plain content. The " (code "sx-on-settle") " hook is a client-side concern — it runs in the global SX environment with access to all primitives."))
|
|
|
|
|
|
|
|
|
|
@@ -348,7 +348,7 @@
|
|
|
|
|
|
|
|
|
|
(~demo-marsh-signal-url)
|
|
|
|
|
|
|
|
|
|
(~doc-code :code (highlight ";; sx-get URL computed from a signal\n(defisland ~demo-marsh-signal-url ()\n (let ((mode (signal \"products\"))\n (query (signal \"\")))\n (div\n ;; Mode selector — changes what we're searching\n (div :class \"flex gap-2\"\n (button :on-click (fn (e) (reset! mode \"products\"))\n :class (computed (fn () ...active-class...))\n \"Products\")\n (button :on-click (fn (e) (reset! mode \"events\")) \"Events\")\n (button :on-click (fn (e) (reset! mode \"posts\")) \"Posts\"))\n\n ;; Search button — URL is a computed expression\n (button :sx-get (computed (fn ()\n (str \"/(geography.(reactive.(api.search-\"\n (deref mode) \")))\" \"?q=\" (deref query))))\n :sx-target \"#signal-results\"\n :sx-swap \"innerHTML\"\n \"Search\")\n\n (div :id \"signal-results\"))))" "lisp"))
|
|
|
|
|
(~doc-code :code (highlight ";; sx-get URL computed from a signal\n(defisland ~demo-marsh-signal-url ()\n (let ((mode (signal \"products\"))\n (query (signal \"\")))\n (div\n ;; Mode selector — changes what we're searching\n (div :class \"flex gap-2\"\n (button :on-click (fn (e) (reset! mode \"products\"))\n :class (computed (fn () ...active-class...))\n \"Products\")\n (button :on-click (fn (e) (reset! mode \"events\")) \"Events\")\n (button :on-click (fn (e) (reset! mode \"posts\")) \"Posts\"))\n\n ;; Search button — URL is a computed expression\n (button :sx-get (computed (fn ()\n (str \"/sx/(geography.(reactive.(api.search-\"\n (deref mode) \")))\" \"?q=\" (deref query))))\n :sx-target \"#signal-results\"\n :sx-swap \"innerHTML\"\n \"Search\")\n\n (div :id \"signal-results\"))))" "lisp"))
|
|
|
|
|
|
|
|
|
|
(p "No custom plumbing. The same " (code "reactive-attr") " mechanism that makes " (code ":class") " reactive also makes " (code ":sx-get") " reactive. " (code "get-verb-info") " reads " (code "dom-get-attr") " at trigger time — it sees the current URL because the effect already updated the DOM attribute."))
|
|
|
|
|
|
|
|
|
|
@@ -361,7 +361,7 @@
|
|
|
|
|
|
|
|
|
|
(~demo-marsh-view-transform)
|
|
|
|
|
|
|
|
|
|
(~doc-code :code (highlight ";; View mode transforms display without refetch\n(defisland ~demo-marsh-view-transform ()\n (let ((view (signal \"list\"))\n (items (signal nil)))\n (div\n ;; View toggle\n (div :class \"flex gap-2\"\n (button :on-click (fn (e) (reset! view \"list\")) \"List\")\n (button :on-click (fn (e) (reset! view \"grid\")) \"Grid\")\n (button :on-click (fn (e) (reset! view \"compact\")) \"Compact\"))\n\n ;; Fetch from server — stores raw data in signal\n (button :sx-get \"/(geography.(reactive.(api.catalog)))\"\n :sx-target \"#catalog-raw\"\n :sx-swap \"innerHTML\"\n \"Fetch Catalog\")\n\n ;; Raw server content (hidden, used as data source)\n (div :id \"catalog-raw\" :class \"hidden\")\n\n ;; Reactive display — re-renders when view changes\n (div (computed (fn () (render-view (deref view) (deref items))))))))" "lisp"))
|
|
|
|
|
(~doc-code :code (highlight ";; View mode transforms display without refetch\n(defisland ~demo-marsh-view-transform ()\n (let ((view (signal \"list\"))\n (items (signal nil)))\n (div\n ;; View toggle\n (div :class \"flex gap-2\"\n (button :on-click (fn (e) (reset! view \"list\")) \"List\")\n (button :on-click (fn (e) (reset! view \"grid\")) \"Grid\")\n (button :on-click (fn (e) (reset! view \"compact\")) \"Compact\"))\n\n ;; Fetch from server — stores raw data in signal\n (button :sx-get \"/sx/(geography.(reactive.(api.catalog)))\"\n :sx-target \"#catalog-raw\"\n :sx-swap \"innerHTML\"\n \"Fetch Catalog\")\n\n ;; Raw server content (hidden, used as data source)\n (div :id \"catalog-raw\" :class \"hidden\")\n\n ;; Reactive display — re-renders when view changes\n (div (computed (fn () (render-view (deref view) (deref items))))))))" "lisp"))
|
|
|
|
|
|
|
|
|
|
(p "The view signal doesn't just toggle CSS classes — it fundamentally reshapes the DOM. List view shows description. Grid view arranges in columns. Compact view shows names only. All from the same server data, transformed by client state."))
|
|
|
|
|
|
|
|
|
|
@@ -408,7 +408,7 @@
|
|
|
|
|
(div :class "border-t border-stone-200 pt-3"
|
|
|
|
|
(div :class "flex items-center gap-3"
|
|
|
|
|
(button :class "px-4 py-2 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
|
|
|
|
:sx-get "/(geography.(reactive.(api.flash-sale)))"
|
|
|
|
|
:sx-get "/sx/(geography.(reactive.(api.flash-sale)))"
|
|
|
|
|
:sx-target "#marsh-server-msg"
|
|
|
|
|
:sx-swap "innerHTML"
|
|
|
|
|
"Fetch Price from Server")
|
|
|
|
|
@@ -471,7 +471,7 @@
|
|
|
|
|
(span :class "text-sm text-stone-600" " times")))
|
|
|
|
|
(div :class "flex items-center gap-3"
|
|
|
|
|
(button :class "px-4 py-2 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
|
|
|
|
:sx-get "/(geography.(reactive.(api.settle-data)))"
|
|
|
|
|
:sx-get "/sx/(geography.(reactive.(api.settle-data)))"
|
|
|
|
|
:sx-target "#settle-result"
|
|
|
|
|
:sx-swap "innerHTML"
|
|
|
|
|
:sx-on-settle "(swap! (use-store \"settle-count\") inc)"
|
|
|
|
|
@@ -516,7 +516,7 @@
|
|
|
|
|
:class "flex-1 px-3 py-1.5 rounded border border-stone-300 text-sm focus:outline-none focus:border-violet-400")
|
|
|
|
|
(button :class "px-4 py-1.5 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
|
|
|
|
:sx-get (computed (fn ()
|
|
|
|
|
(str "/(geography.(reactive.(api.search-" (deref mode)
|
|
|
|
|
(str "/sx/(geography.(reactive.(api.search-" (deref mode)
|
|
|
|
|
")))""?q=" (deref query))))
|
|
|
|
|
:sx-target "#signal-results"
|
|
|
|
|
:sx-swap "innerHTML"
|
|
|
|
|
@@ -524,7 +524,7 @@
|
|
|
|
|
;; Current URL display
|
|
|
|
|
(p :class "text-xs text-stone-400 font-mono"
|
|
|
|
|
"URL: " (computed (fn ()
|
|
|
|
|
(str "/(geography.(reactive.(api.search-" (deref mode) ")))" "?q=" (deref query)))))
|
|
|
|
|
(str "/sx/(geography.(reactive.(api.search-" (deref mode) ")))" "?q=" (deref query)))))
|
|
|
|
|
;; Results
|
|
|
|
|
(div :id "signal-results" :class "min-h-[3rem] rounded bg-stone-50 p-2"
|
|
|
|
|
(p :class "text-sm text-stone-400 italic" "Select a category and search.")))))
|
|
|
|
|
@@ -561,7 +561,7 @@
|
|
|
|
|
;; Fetch button — response writes structured data to store via data-init
|
|
|
|
|
(div :class "flex items-center gap-3"
|
|
|
|
|
(button :class "px-4 py-2 rounded bg-emerald-600 text-white text-sm font-medium hover:bg-emerald-700"
|
|
|
|
|
:sx-get "/(geography.(reactive.(api.catalog)))"
|
|
|
|
|
:sx-get "/sx/(geography.(reactive.(api.catalog)))"
|
|
|
|
|
:sx-target "#catalog-msg"
|
|
|
|
|
:sx-swap "innerHTML"
|
|
|
|
|
"Fetch Catalog")
|
|
|
|
|
|