SX docs: configurable shell, SX-native event handlers, nav fixes
- Configurable page shell (~sx-page-shell kwargs + SX_SHELL app config) so each app controls its own assets — sx docs loads only sx-browser.js - SX-evaluated sx-on:* handlers (eval-expr instead of new Function) with DOM primitives registered in PRIMITIVES table - data-init boot mode for pure SX initialization scripts - Jiggle animation on links while fetching - Nav: 3-column grid for centered alignment, is-leaf sizing, fix map-indexed param order (index, item), guard mod-by-zero - Async route eval failure now falls back to server fetch instead of silently rendering nothing - Remove duplicate h1 title from ~doc-page - Re-bootstrap sx-ref.js + sx-browser.js Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -109,12 +109,22 @@
|
||||
(fn (el verb method url extraParams)
|
||||
;; Execute the actual fetch. Manages abort, headers, body, loading state.
|
||||
(let ((sync (dom-get-attr el "sx-sync")))
|
||||
;; Abort previous if sync mode
|
||||
;; Abort previous if sync mode (per-element)
|
||||
(when (= sync "replace")
|
||||
(abort-previous el))
|
||||
|
||||
;; Abort any in-flight request targeting the same swap target.
|
||||
;; This ensures rapid navigation (click A then B) cancels A's fetch.
|
||||
(let ((target-el (resolve-target el)))
|
||||
(when target-el
|
||||
(abort-previous-target target-el)))
|
||||
|
||||
(let ((ctrl (new-abort-controller)))
|
||||
(track-controller el ctrl)
|
||||
;; Also track against the swap target for cross-element cancellation
|
||||
(let ((target-el (resolve-target el)))
|
||||
(when target-el
|
||||
(track-controller-target target-el ctrl)))
|
||||
|
||||
;; Build request
|
||||
(let ((body-info (build-request-body el method url))
|
||||
@@ -909,7 +919,9 @@
|
||||
(try-async-eval-content content-src env
|
||||
(fn (rendered)
|
||||
(if (nil? rendered)
|
||||
(log-warn (str "sx:route async eval failed for " pathname))
|
||||
(do (log-warn (str "sx:route cache+async eval failed for " pathname " — server fallback"))
|
||||
(fetch-and-restore target pathname
|
||||
(build-request-headers target (loaded-component-names) _css-hash) 0))
|
||||
(swap-rendered-content target rendered pathname))))
|
||||
true)
|
||||
;; Sync render (data only)
|
||||
@@ -932,12 +944,16 @@
|
||||
(try-async-eval-content content-src env
|
||||
(fn (rendered)
|
||||
(if (nil? rendered)
|
||||
(log-warn (str "sx:route data+async eval failed for " pathname))
|
||||
(do (log-warn (str "sx:route data+async eval failed for " pathname " — server fallback"))
|
||||
(fetch-and-restore target pathname
|
||||
(build-request-headers target (loaded-component-names) _css-hash) 0))
|
||||
(swap-rendered-content target rendered pathname))))
|
||||
;; Sync render (data only)
|
||||
(let ((rendered (try-eval-content content-src env)))
|
||||
(if (nil? rendered)
|
||||
(log-warn (str "sx:route data eval failed for " pathname))
|
||||
(do (log-warn (str "sx:route data eval failed for " pathname " — server fallback"))
|
||||
(fetch-and-restore target pathname
|
||||
(build-request-headers target (loaded-component-names) _css-hash) 0))
|
||||
(swap-rendered-content target rendered pathname)))))))
|
||||
true)))
|
||||
;; Non-data page
|
||||
@@ -948,7 +964,9 @@
|
||||
(try-async-eval-content content-src (merge closure params)
|
||||
(fn (rendered)
|
||||
(if (nil? rendered)
|
||||
(log-warn (str "sx:route async eval failed for " pathname))
|
||||
(do (log-warn (str "sx:route async eval failed for " pathname " — server fallback"))
|
||||
(fetch-and-restore target pathname
|
||||
(build-request-headers target (loaded-component-names) _css-hash) 0))
|
||||
(swap-rendered-content target rendered pathname))))
|
||||
true)
|
||||
;; Pure page: render immediately
|
||||
@@ -1033,7 +1051,9 @@
|
||||
|
||||
(define bind-inline-handlers
|
||||
(fn (root)
|
||||
;; Find elements with sx-on:* attributes and bind handlers
|
||||
;; Find elements with sx-on:* attributes and bind SX event handlers.
|
||||
;; Handler bodies are SX expressions evaluated with `event` and `this`
|
||||
;; bound in scope. No raw JS — handlers are pure SX.
|
||||
(for-each
|
||||
(fn (el)
|
||||
(for-each
|
||||
@@ -1044,9 +1064,19 @@
|
||||
(let ((event-name (slice name 6)))
|
||||
(when (not (is-processed? el (str "on:" event-name)))
|
||||
(mark-processed! el (str "on:" event-name))
|
||||
(bind-inline-handler el event-name body))))))
|
||||
;; Parse body as SX, bind handler that evaluates it
|
||||
(let ((exprs (sx-parse body)))
|
||||
(dom-listen el event-name
|
||||
(fn (e)
|
||||
(let ((handler-env (env-extend (dict))))
|
||||
(env-set! handler-env "event" e)
|
||||
(env-set! handler-env "this" el)
|
||||
(env-set! handler-env "detail" (event-detail e))
|
||||
(for-each
|
||||
(fn (expr) (eval-expr expr handler-env))
|
||||
exprs))))))))))
|
||||
(dom-attr-list el)))
|
||||
(dom-query-all (or root (dom-body)) "[sx-on\\:beforeRequest],[sx-on\\:afterRequest],[sx-on\\:afterSwap],[sx-on\\:afterSettle],[sx-on\\:load]"))))
|
||||
(dom-query-all (or root (dom-body)) "[sx-on\\:]"))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
@@ -1209,6 +1239,8 @@
|
||||
;; === Abort controllers ===
|
||||
;; (abort-previous el) → abort + remove controller for element
|
||||
;; (track-controller el ctrl) → store controller for element
|
||||
;; (abort-previous-target el) → abort + remove controller for target element
|
||||
;; (track-controller-target el c) → store controller keyed by target element
|
||||
;; (new-abort-controller) → new AbortController()
|
||||
;; (controller-signal ctrl) → ctrl.signal
|
||||
;; (abort-error? err) → boolean (err.name === "AbortError")
|
||||
@@ -1274,7 +1306,7 @@
|
||||
;; (bind-client-route-click link href fallback-fn) → void (client route click handler)
|
||||
;;
|
||||
;; === Inline handlers ===
|
||||
;; (bind-inline-handler el event-name body) → void (new Function)
|
||||
;; (sx-on:* handlers are now evaluated as SX, not delegated to platform)
|
||||
;;
|
||||
;; === Preload ===
|
||||
;; (bind-preload el events debounce-ms fn) → void
|
||||
|
||||
Reference in New Issue
Block a user