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:
@@ -737,6 +737,26 @@ document.body.addEventListener('click', function (e) {
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Client-side route nav selection
|
||||
// - Updates aria-selected on sub-nav links after client-side routing
|
||||
// - Scoped to <nav> elements (top-level section links are outside <nav>)
|
||||
// ============================================================================
|
||||
|
||||
document.body.addEventListener('sx:clientRoute', function (e) {
|
||||
var pathname = e.detail && e.detail.pathname;
|
||||
if (!pathname) return;
|
||||
// Deselect all sub-nav links (inside <nav> elements)
|
||||
document.querySelectorAll('nav a[aria-selected]').forEach(function (a) {
|
||||
a.setAttribute('aria-selected', 'false');
|
||||
});
|
||||
// Select the matching link
|
||||
document.querySelectorAll('nav a[href="' + pathname + '"]').forEach(function (a) {
|
||||
a.setAttribute('aria-selected', 'true');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Scrolling menu arrow visibility (replaces hyperscript scroll/load handlers)
|
||||
// Elements with data-scroll-arrows="arrow-class" show/hide arrows on overflow.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// =========================================================================
|
||||
|
||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||
var SX_VERSION = "2026-03-06T22:18:52Z";
|
||||
var SX_VERSION = "2026-03-06T23:02:44Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -1850,7 +1850,7 @@ return postSwap(target); });
|
||||
var clientRouted = false;
|
||||
if (isSxTruthy(isGetLink)) {
|
||||
logInfo((String("sx:route trying ") + String(get(verbInfo, "url"))));
|
||||
clientRouted = tryClientRoute(urlPathname(get(verbInfo, "url")));
|
||||
clientRouted = tryClientRoute(urlPathname(get(verbInfo, "url")), domGetAttr(el, "sx-target"));
|
||||
}
|
||||
return (isSxTruthy(clientRouted) ? (browserPushState(get(verbInfo, "url")), browserScrollTo(0, 0)) : ((isSxTruthy(isGetLink) ? logInfo((String("sx:route server fetch ") + String(get(verbInfo, "url")))) : NIL), (isSxTruthy(get(mods, "delay")) ? (clearTimeout_(timer), (timer = setTimeout_(function() { return executeRequest(el, verbInfo, NIL); }, get(mods, "delay")))) : executeRequest(el, verbInfo, NIL))));
|
||||
})()) : NIL);
|
||||
@@ -1902,10 +1902,12 @@ return domAppendToHead(link); }, domQueryAll(container, "link[rel=\"stylesheet\"
|
||||
var processBoosted = function(root) { return forEach(function(container) { return boostDescendants(container); }, domQueryAll(sxOr(root, domBody()), "[sx-boost]")); };
|
||||
|
||||
// boost-descendants
|
||||
var boostDescendants = function(container) { { var _c = domQueryAll(container, "a[href]"); for (var _i = 0; _i < _c.length; _i++) { var link = _c[_i]; if (isSxTruthy((isSxTruthy(!isSxTruthy(isProcessed(link, "boost"))) && shouldBoostLink(link)))) {
|
||||
var boostDescendants = function(container) { return (function() {
|
||||
var boostTarget = domGetAttr(container, "sx-boost");
|
||||
{ var _c = domQueryAll(container, "a[href]"); for (var _i = 0; _i < _c.length; _i++) { var link = _c[_i]; if (isSxTruthy((isSxTruthy(!isSxTruthy(isProcessed(link, "boost"))) && shouldBoostLink(link)))) {
|
||||
markProcessed(link, "boost");
|
||||
if (isSxTruthy(!isSxTruthy(domHasAttr(link, "sx-target")))) {
|
||||
domSetAttr(link, "sx-target", "#main-panel");
|
||||
if (isSxTruthy((isSxTruthy(!isSxTruthy(domHasAttr(link, "sx-target"))) && isSxTruthy(boostTarget) && !isSxTruthy((boostTarget == "true"))))) {
|
||||
domSetAttr(link, "sx-target", boostTarget);
|
||||
}
|
||||
if (isSxTruthy(!isSxTruthy(domHasAttr(link, "sx-swap")))) {
|
||||
domSetAttr(link, "sx-swap", "innerHTML");
|
||||
@@ -1915,20 +1917,21 @@ return domAppendToHead(link); }, domQueryAll(container, "link[rel=\"stylesheet\"
|
||||
}
|
||||
bindClientRouteLink(link, domGetAttr(link, "href"));
|
||||
} } }
|
||||
return forEach(function(form) { return (isSxTruthy((isSxTruthy(!isSxTruthy(isProcessed(form, "boost"))) && shouldBoostForm(form))) ? (markProcessed(form, "boost"), (function() {
|
||||
return forEach(function(form) { return (isSxTruthy((isSxTruthy(!isSxTruthy(isProcessed(form, "boost"))) && shouldBoostForm(form))) ? (markProcessed(form, "boost"), (function() {
|
||||
var method = upper(sxOr(domGetAttr(form, "method"), "GET"));
|
||||
var action = sxOr(domGetAttr(form, "action"), browserLocationHref());
|
||||
if (isSxTruthy(!isSxTruthy(domHasAttr(form, "sx-target")))) {
|
||||
domSetAttr(form, "sx-target", "#main-panel");
|
||||
if (isSxTruthy((isSxTruthy(!isSxTruthy(domHasAttr(form, "sx-target"))) && isSxTruthy(boostTarget) && !isSxTruthy((boostTarget == "true"))))) {
|
||||
domSetAttr(form, "sx-target", boostTarget);
|
||||
}
|
||||
if (isSxTruthy(!isSxTruthy(domHasAttr(form, "sx-swap")))) {
|
||||
domSetAttr(form, "sx-swap", "innerHTML");
|
||||
}
|
||||
return bindBoostForm(form, method, action);
|
||||
})()) : NIL); }, domQueryAll(container, "form")); };
|
||||
})()) : NIL); }, domQueryAll(container, "form"));
|
||||
})(); };
|
||||
|
||||
// try-client-route
|
||||
var tryClientRoute = function(pathname) { return (function() {
|
||||
var tryClientRoute = function(pathname, targetSel) { return (function() {
|
||||
var match = findMatchingRoute(pathname, _pageRoutes);
|
||||
return (isSxTruthy(isNil(match)) ? (logInfo((String("sx:route no match (") + String(len(_pageRoutes)) + String(" routes) ") + String(pathname))), false) : (isSxTruthy(get(match, "has-data")) ? (logInfo((String("sx:route server (has data) ") + String(pathname))), false) : (function() {
|
||||
var contentSrc = get(match, "content");
|
||||
@@ -1938,8 +1941,8 @@ return forEach(function(form) { return (isSxTruthy((isSxTruthy(!isSxTruthy(isPro
|
||||
var env = merge(closure, params);
|
||||
var rendered = tryEvalContent(contentSrc, env);
|
||||
return (isSxTruthy(isNil(rendered)) ? (logInfo((String("sx:route server (eval failed) ") + String(pathname))), false) : (function() {
|
||||
var target = domQueryById("main-panel");
|
||||
return (isSxTruthy(isNil(target)) ? (logWarn("sx:route #main-panel not found"), false) : (domSetTextContent(target, ""), domAppend(target, rendered), hoistHeadElementsFull(target), processElements(target), sxHydrateElements(target), logInfo((String("sx:route client ") + String(pathname))), true));
|
||||
var target = (isSxTruthy((isSxTruthy(targetSel) && !isSxTruthy((targetSel == "true")))) ? domQuery(targetSel) : NIL);
|
||||
return (isSxTruthy(isNil(target)) ? (logWarn((String("sx:route target not found: ") + String(targetSel))), false) : (domSetTextContent(target, ""), domAppend(target, rendered), hoistHeadElementsFull(target), processElements(target), sxHydrateElements(target), domDispatch(target, "sx:clientRoute", {["pathname"]: pathname}), logInfo((String("sx:route client ") + String(pathname))), true));
|
||||
})());
|
||||
})());
|
||||
})()));
|
||||
@@ -2026,14 +2029,16 @@ return bindInlineHandlers(root); };
|
||||
|
||||
// handle-popstate
|
||||
var handlePopstate = function(scrollY) { return (function() {
|
||||
var main = domQueryById("main-panel");
|
||||
var boostEl = domQuery("[sx-boost]");
|
||||
var url = browserLocationHref();
|
||||
return (isSxTruthy(main) ? (function() {
|
||||
return (isSxTruthy(boostEl) ? (function() {
|
||||
var targetSel = domGetAttr(boostEl, "sx-boost");
|
||||
var target = (isSxTruthy((isSxTruthy(targetSel) && !isSxTruthy((targetSel == "true")))) ? domQuery(targetSel) : NIL);
|
||||
var pathname = urlPathname(url);
|
||||
return (isSxTruthy(tryClientRoute(pathname)) ? browserScrollTo(0, scrollY) : (function() {
|
||||
var headers = buildRequestHeaders(main, loadedComponentNames(), _cssHash);
|
||||
return fetchAndRestore(main, url, headers, scrollY);
|
||||
})());
|
||||
return (isSxTruthy(target) ? (isSxTruthy(tryClientRoute(pathname, targetSel)) ? browserScrollTo(0, scrollY) : (function() {
|
||||
var headers = buildRequestHeaders(target, loadedComponentNames(), _cssHash);
|
||||
return fetchAndRestore(target, url, headers, scrollY);
|
||||
})()) : NIL);
|
||||
})() : NIL);
|
||||
})(); };
|
||||
|
||||
@@ -3106,7 +3111,7 @@ callExpr.push(dictGet(kwargs, k)); } }
|
||||
}
|
||||
return sxRenderWithEnv(source, merged);
|
||||
} catch (e) {
|
||||
console.error("[sx-ref] sx:route eval error for:", source, e);
|
||||
logInfo("sx:route eval miss: " + (e && e.message ? e.message : e));
|
||||
return NIL;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user