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

@@ -170,6 +170,12 @@
;; nil or false → skip
(or (nil? attr-val) (= attr-val false))
nil
;; Event handler: on-click, on-submit, on-input, etc.
;; Value must be callable (lambda/function)
(and (starts-with? attr-name "on-")
(callable? attr-val))
(let ((event-name (substring attr-name 3 (string-length attr-name))))
(dom-listen el event-name attr-val))
;; Boolean attr
(contains? BOOLEAN_ATTRS attr-name)
(when attr-val (dom-set-attr el attr-name ""))
@@ -589,6 +595,11 @@
;; (dom-child-nodes frag) → list of child nodes
;; (dom-remove-children-after m)→ void (remove all siblings after marker)
;; (dom-set-data el key val) → void (store arbitrary data on element)
;; (dom-get-data el key) → any (retrieve data stored on element)
;;
;; Event handling:
;; (dom-listen el name handler) → remove-fn (addEventListener, returns remover)
;; (dom-dispatch el name detail)→ boolean (dispatch CustomEvent, bubbles: true)
;;
;; Content parsing:
;; (dom-parse-html s) → DocumentFragment from HTML string

View File

@@ -397,6 +397,7 @@ class JSEmitter:
"bind-sse": "bindSse",
"bind-sse-swap": "bindSseSwap",
"bind-inline-handlers": "bindInlineHandlers",
"process-emit-elements": "processEmitElements",
"bind-preload-for": "bindPreloadFor",
"do-preload": "doPreload",
"VERB_SELECTOR": "VERB_SELECTOR",

View File

@@ -1087,10 +1087,11 @@
(mark-processed! el "verb")
(process-one el)))
els))
;; Also process boost, SSE, inline handlers
;; Also process boost, SSE, inline handlers, emit attributes
(process-boosted root)
(process-sse root)
(bind-inline-handlers root)))
(bind-inline-handlers root)
(process-emit-elements root)))
(define process-one
@@ -1104,6 +1105,40 @@
(bind-preload-for el))))))
;; --------------------------------------------------------------------------
;; data-sx-emit — auto-dispatch custom events for lake→island bridge
;; --------------------------------------------------------------------------
;;
;; Elements with data-sx-emit="event-name" get a click listener that
;; dispatches a CustomEvent with that name. Optional data-sx-emit-detail
;; provides JSON payload.
;;
;; Example:
;; <button data-sx-emit="cart:add"
;; data-sx-emit-detail='{"id":42,"name":"Widget"}'>
;; Add to Cart
;; </button>
;;
;; On click → dispatches CustomEvent "cart:add" with detail {id:42, name:"Widget"}
;; The event bubbles up to the island container where bridge-event catches it.
(define process-emit-elements
(fn (root)
(let ((els (dom-query-all (or root (dom-body)) "[data-sx-emit]")))
(for-each
(fn (el)
(when (not (is-processed? el "emit"))
(mark-processed! el "emit")
(let ((event-name (dom-get-attr el "data-sx-emit")))
(when event-name
(dom-listen el "click"
(fn (e)
(let ((detail-json (dom-get-attr el "data-sx-emit-detail"))
(detail (if detail-json (json-parse detail-json) (dict))))
(dom-dispatch el event-name detail))))))))
els))))
;; --------------------------------------------------------------------------
;; History: popstate handler
;; --------------------------------------------------------------------------