Add event bindings and data-sx-emit processing

- adapter-dom.sx: detect :on-click/:on-submit etc. in render-dom-element
  — if attr starts with "on-" and value is callable, wire via dom-listen
- orchestration.sx: add process-emit-elements for data-sx-emit attrs
  — auto-dispatch custom events on click with optional JSON detail
- bootstrap_js.py: add processEmitElements RENAME
- Regenerate sx-ref.js with all changes
- Update reactive-islands status table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 11:15:20 +00:00
parent c55f0956bc
commit e15b5c9dbc
5 changed files with 75 additions and 7 deletions

View File

@@ -14,7 +14,7 @@
// =========================================================================
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
var SX_VERSION = "2026-03-08T11:12:31Z";
var SX_VERSION = "2026-03-08T11:14:47Z";
function isNil(x) { return x === NIL || x === null || x === undefined; }
function isSxTruthy(x) { return x !== false && !isNil(x); }
@@ -1490,7 +1490,10 @@ continue; } else { return NIL; } } };
return (isSxTruthy(skip) ? assoc(state, "skip", false, "i", (get(state, "i") + 1)) : (isSxTruthy((isSxTruthy((typeOf(arg) == "keyword")) && ((get(state, "i") + 1) < len(args)))) ? (function() {
var attrName = keywordName(arg);
var attrVal = trampoline(evalExpr(nth(args, (get(state, "i") + 1)), env));
(isSxTruthy(sxOr(isNil(attrVal), (attrVal == false))) ? NIL : (isSxTruthy(contains(BOOLEAN_ATTRS, attrName)) ? (isSxTruthy(attrVal) ? domSetAttr(el, attrName, "") : NIL) : (isSxTruthy((attrVal == true)) ? domSetAttr(el, attrName, "") : domSetAttr(el, attrName, (String(attrVal))))));
(isSxTruthy(sxOr(isNil(attrVal), (attrVal == false))) ? NIL : (isSxTruthy((isSxTruthy(startsWith(attrName, "on-")) && isCallable(attrVal))) ? (function() {
var eventName = substring(attrName, 3, stringLength(attrName));
return domListen(el, eventName, attrVal);
})() : (isSxTruthy(contains(BOOLEAN_ATTRS, attrName)) ? (isSxTruthy(attrVal) ? domSetAttr(el, attrName, "") : NIL) : (isSxTruthy((attrVal == true)) ? domSetAttr(el, attrName, "") : domSetAttr(el, attrName, (String(attrVal)))))));
return assoc(state, "skip", true, "i", (get(state, "i") + 1));
})() : ((isSxTruthy(!isSxTruthy(contains(VOID_ELEMENTS, tag))) ? domAppend(el, renderToDom(arg, env, newNs)) : NIL), assoc(state, "i", (get(state, "i") + 1)))));
})(); }, {["i"]: 0, ["skip"]: false}, args);
@@ -2496,7 +2499,8 @@ return postSwap(target); })) : NIL);
})();
processBoosted(root);
processSse(root);
return bindInlineHandlers(root); };
bindInlineHandlers(root);
return processEmitElements(root); };
// process-one
var processOne = function(el) { return (function() {
@@ -2504,6 +2508,19 @@ return bindInlineHandlers(root); };
return (isSxTruthy(verbInfo) ? (isSxTruthy(!isSxTruthy(domHasAttr(el, "sx-disable"))) ? (bindTriggers(el, verbInfo), bindPreloadFor(el)) : NIL) : NIL);
})(); };
// process-emit-elements
var processEmitElements = function(root) { return (function() {
var els = domQueryAll(sxOr(root, domBody()), "[data-sx-emit]");
return forEach(function(el) { return (isSxTruthy(!isSxTruthy(isProcessed(el, "emit"))) ? (markProcessed(el, "emit"), (function() {
var eventName = domGetAttr(el, "data-sx-emit");
return (isSxTruthy(eventName) ? domListen(el, "click", function(e) { return (function() {
var detailJson = domGetAttr(el, "data-sx-emit-detail");
var detail = (isSxTruthy(detailJson) ? jsonParse(detailJson) : {});
return domDispatch(el, eventName, detail);
})(); }) : NIL);
})()) : NIL); }, els);
})(); };
// handle-popstate
var handlePopstate = function(scrollY) { return (function() {
var url = browserLocationHref();