Update reference docs: fix event names, add demos, document sx-boost target
- Remove sx:afterSettle (not dispatched), rename sx:sendError → sx:requestError - Add sx:clientRoute event (Phase 3 client-side routing) - Add working demos for all 10 events (afterRequest, afterSwap, requestError, clientRoute, sseOpen, sseMessage, sseError were missing demos) - Update sx-boost docs: configurable target selector, client routing behavior - Remove app-specific nav logic from orchestration.sx, use sx:clientRoute event - Pass page content deps to sx_response for component loading after server fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -113,7 +113,7 @@ BEHAVIOR_ATTRS = [
|
||||
("sx-media", "Only enable this element when the media query matches", True),
|
||||
("sx-disable", "Disable sx processing on this element and its children", True),
|
||||
("sx-on:*", "Inline event handler — e.g. sx-on:click runs JavaScript on event", True),
|
||||
("sx-boost", "Progressively enhance all links and forms in a container with AJAX navigation", True),
|
||||
("sx-boost", "Progressively enhance all links and forms in a container with AJAX navigation. Value can be a target selector.", True),
|
||||
("sx-preload", "Preload content on hover/focus for instant response on click", True),
|
||||
("sx-preserve", "Preserve element across swaps — keeps DOM state, event listeners, and scroll position", True),
|
||||
("sx-indicator", "CSS selector for a loading indicator element to show/hide during requests", True),
|
||||
@@ -171,10 +171,10 @@ EVENTS = [
|
||||
("sx:beforeRequest", "Fired before an sx request is issued. Call preventDefault() to cancel."),
|
||||
("sx:afterRequest", "Fired after a successful sx response is received."),
|
||||
("sx:afterSwap", "Fired after the response has been swapped into the DOM."),
|
||||
("sx:afterSettle", "Fired after the DOM has settled (scripts executed, etc)."),
|
||||
("sx:responseError", "Fired on HTTP error responses (4xx, 5xx)."),
|
||||
("sx:sendError", "Fired when the request fails to send (network error)."),
|
||||
("sx:requestError", "Fired when the request fails to send (network error, abort)."),
|
||||
("sx:validationFailed", "Fired when sx-validate blocks a request due to invalid form data."),
|
||||
("sx:clientRoute", "Fired after successful client-side routing (no server request)."),
|
||||
("sx:sseOpen", "Fired when an SSE connection is established."),
|
||||
("sx:sseMessage", "Fired when an SSE message is received and swapped."),
|
||||
("sx:sseError", "Fired when an SSE connection encounters an error."),
|
||||
@@ -585,7 +585,7 @@ EVENT_DETAILS: dict[str, dict] = {
|
||||
"sx:afterRequest": {
|
||||
"description": (
|
||||
"Fired on the triggering element after a successful sx response is received, "
|
||||
"before the swap happens. The response data is available on event.detail. "
|
||||
"before the swap happens. event.detail contains the response status. "
|
||||
"Use this for logging, analytics, or pre-swap side effects."
|
||||
),
|
||||
"example": (
|
||||
@@ -595,42 +595,27 @@ EVENT_DETAILS: dict[str, dict] = {
|
||||
' :sx-on:sx:afterRequest "console.log(\'Response received\', event.detail)"\n'
|
||||
' "Load data")'
|
||||
),
|
||||
"demo": "ref-event-after-request-demo",
|
||||
},
|
||||
"sx:afterSwap": {
|
||||
"description": (
|
||||
"Fired after the response content has been swapped into the DOM. "
|
||||
"The new content is in place but scripts may not have executed yet. "
|
||||
"Use this to initialize UI on newly inserted content."
|
||||
"Fired on the triggering element after the response content has been "
|
||||
"swapped into the DOM. event.detail contains the target element and swap "
|
||||
"style. Use this to initialize UI on newly inserted content."
|
||||
),
|
||||
"example": (
|
||||
';; Initialize tooltips on new content\n'
|
||||
'(div :sx-on:sx:afterSwap "initTooltips(this)"\n'
|
||||
' (button :sx-get "/api/items"\n'
|
||||
' :sx-target "#item-list"\n'
|
||||
' "Load items")\n'
|
||||
' (div :id "item-list"))'
|
||||
';; Run code after content is swapped in\n'
|
||||
'(button :sx-get "/api/items"\n'
|
||||
' :sx-target "#item-list"\n'
|
||||
' :sx-on:sx:afterSwap "console.log(\'Swapped into\', event.detail.target)"\n'
|
||||
' "Load items")'
|
||||
),
|
||||
},
|
||||
"sx:afterSettle": {
|
||||
"description": (
|
||||
"Fired after the DOM has fully settled — all scripts executed, transitions "
|
||||
"complete. This is the safest point to run code that depends on the final "
|
||||
"state of the DOM after a swap."
|
||||
),
|
||||
"example": (
|
||||
';; Scroll to new content after settle\n'
|
||||
'(div :sx-on:sx:afterSettle "document.getElementById(\'new-item\').scrollIntoView()"\n'
|
||||
' (button :sx-get "/api/append"\n'
|
||||
' :sx-target "#list" :sx-swap "beforeend"\n'
|
||||
' "Add item")\n'
|
||||
' (div :id "list"))'
|
||||
),
|
||||
"demo": "ref-event-after-settle-demo",
|
||||
"demo": "ref-event-after-swap-demo",
|
||||
},
|
||||
"sx:responseError": {
|
||||
"description": (
|
||||
"Fired when the server responds with an HTTP error (4xx or 5xx). "
|
||||
"event.detail contains the status code and response. "
|
||||
"event.detail contains the status code and response text. "
|
||||
"Use this for error handling, showing notifications, or retry logic."
|
||||
),
|
||||
"example": (
|
||||
@@ -643,21 +628,22 @@ EVENT_DETAILS: dict[str, dict] = {
|
||||
),
|
||||
"demo": "ref-event-response-error-demo",
|
||||
},
|
||||
"sx:sendError": {
|
||||
"sx:requestError": {
|
||||
"description": (
|
||||
"Fired when the request fails to send — typically a network error, "
|
||||
"DNS failure, or CORS issue. Unlike sx:responseError, no HTTP response "
|
||||
"was received at all."
|
||||
"was received at all. Aborted requests (e.g. from sx-sync) do not fire this event."
|
||||
),
|
||||
"example": (
|
||||
';; Handle network failures\n'
|
||||
'(div :sx-on:sx:sendError "this.querySelector(\'.status\').textContent = \'Offline\'"\n'
|
||||
'(div :sx-on:sx:requestError "this.querySelector(\'.status\').textContent = \'Offline\'"\n'
|
||||
' (button :sx-get "/api/data"\n'
|
||||
' :sx-target "#result"\n'
|
||||
' "Load")\n'
|
||||
' (span :class "status")\n'
|
||||
' (div :id "result"))'
|
||||
),
|
||||
"demo": "ref-event-request-error-demo",
|
||||
},
|
||||
"sx:validationFailed": {
|
||||
"description": (
|
||||
@@ -676,6 +662,29 @@ EVENT_DETAILS: dict[str, dict] = {
|
||||
),
|
||||
"demo": "ref-event-validation-failed-demo",
|
||||
},
|
||||
"sx:clientRoute": {
|
||||
"description": (
|
||||
"Fired on the swap target after successful client-side routing. "
|
||||
"No server request was made — the page was rendered entirely in the browser "
|
||||
"from component definitions the client already has. "
|
||||
"event.detail contains the pathname. Use this to update navigation state, "
|
||||
"analytics, or other side effects that should run on client-only navigation. "
|
||||
"The event bubbles, so you can listen on document.body."
|
||||
),
|
||||
"example": (
|
||||
';; Pages with no :data are client-routable.\n'
|
||||
';; sx-boost containers try client routing first.\n'
|
||||
';; On success, sx:clientRoute fires on the swap target.\n'
|
||||
'(nav :sx-boost "#main-panel"\n'
|
||||
' (a :href "/essays/" "Essays")\n'
|
||||
' (a :href "/plans/" "Plans"))\n'
|
||||
'\n'
|
||||
';; Listen in body.js:\n'
|
||||
';; document.body.addEventListener("sx:clientRoute",\n'
|
||||
';; function(e) { updateNav(e.detail.pathname); })'
|
||||
),
|
||||
"demo": "ref-event-client-route-demo",
|
||||
},
|
||||
"sx:sseOpen": {
|
||||
"description": (
|
||||
"Fired when a Server-Sent Events connection is successfully established. "
|
||||
@@ -688,6 +697,7 @@ EVENT_DETAILS: dict[str, dict] = {
|
||||
' (span :class "status" "Connecting...")\n'
|
||||
' (div :id "messages"))'
|
||||
),
|
||||
"demo": "ref-event-sse-open-demo",
|
||||
},
|
||||
"sx:sseMessage": {
|
||||
"description": (
|
||||
@@ -698,10 +708,10 @@ EVENT_DETAILS: dict[str, dict] = {
|
||||
';; Count received messages\n'
|
||||
'(div :sx-sse "/api/stream"\n'
|
||||
' :sx-sse-swap "update"\n'
|
||||
' :sx-on:sx:sseMessage "this.dataset.count = (parseInt(this.dataset.count||0)+1); this.querySelector(\'.count\').textContent = this.dataset.count"\n'
|
||||
' (span :class "count" "0") " messages received"\n'
|
||||
' (div :id "stream-content"))'
|
||||
' :sx-on:sx:sseMessage "this.dataset.count = (parseInt(this.dataset.count||0)+1)"\n'
|
||||
' (span :class "count" "0") " messages received")'
|
||||
),
|
||||
"demo": "ref-event-sse-message-demo",
|
||||
},
|
||||
"sx:sseError": {
|
||||
"description": (
|
||||
@@ -715,6 +725,7 @@ EVENT_DETAILS: dict[str, dict] = {
|
||||
' (span :class "status" "Connecting...")\n'
|
||||
' (div :id "messages"))'
|
||||
),
|
||||
"demo": "ref-event-sse-error-demo",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1200,14 +1211,22 @@ ATTR_DETAILS: dict[str, dict] = {
|
||||
"description": (
|
||||
"Progressively enhance all descendant links and forms with AJAX navigation. "
|
||||
"Links become sx-get requests with pushState, forms become sx-post/sx-get requests. "
|
||||
"No explicit sx-* attributes needed on each link or form — just place sx-boost on a container."
|
||||
"No explicit sx-* attributes needed on each link or form — just place sx-boost on a container. "
|
||||
'The attribute value can be a CSS selector (e.g. sx-boost="#main-panel") to set '
|
||||
"the default swap target for all boosted descendants. If set to \"true\", "
|
||||
"each link/form must specify its own sx-target. "
|
||||
"Pure pages (no server data dependencies) are rendered client-side without a server request."
|
||||
),
|
||||
"demo": "ref-boost-demo",
|
||||
"example": (
|
||||
'(nav :sx-boost "true"\n'
|
||||
';; Boost with configurable target\n'
|
||||
'(nav :sx-boost "#main-panel"\n'
|
||||
' (a :href "/docs/introduction" "Introduction")\n'
|
||||
' (a :href "/docs/components" "Components")\n'
|
||||
' (a :href "/docs/evaluator" "Evaluator"))'
|
||||
' (a :href "/docs/evaluator" "Evaluator"))\n'
|
||||
'\n'
|
||||
';; All links swap into #main-panel automatically.\n'
|
||||
';; Pure pages render client-side (no server request).'
|
||||
),
|
||||
},
|
||||
"sx-preload": {
|
||||
|
||||
Reference in New Issue
Block a user