Merge branch 'worktree-api-urls' into macros
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
// =========================================================================
|
||||
|
||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||
var SX_VERSION = "2026-03-13T04:50:39Z";
|
||||
var SX_VERSION = "2026-03-13T05:11:12Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -1934,7 +1934,7 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragme
|
||||
return assoc(state, "skip", true, "i", (get(state, "i") + 1));
|
||||
})() : ((isSxTruthy(!isSxTruthy(contains(VOID_ELEMENTS, tag))) ? (function() {
|
||||
var child = renderToDom(arg, env, newNs);
|
||||
return (isSxTruthy(isSpread(child)) ? forEach(function(key) { return (function() {
|
||||
return (isSxTruthy((isSxTruthy(isSpread(child)) && _islandScope)) ? reactiveSpread(el, function() { return renderToDom(arg, env, newNs); }) : (isSxTruthy(isSpread(child)) ? forEach(function(key) { return (function() {
|
||||
var val = dictGet(spreadAttrs(child), key);
|
||||
return (isSxTruthy((key == "class")) ? (function() {
|
||||
var existing = domGetAttr(el, "class");
|
||||
@@ -1943,7 +1943,7 @@ return (function() { var _m = typeOf(expr); if (_m == "nil") return createFragme
|
||||
var existing = domGetAttr(el, "style");
|
||||
return domSetAttr(el, "style", (isSxTruthy((isSxTruthy(existing) && !isSxTruthy((existing == "")))) ? (String(existing) + String(";") + String(val)) : val));
|
||||
})() : domSetAttr(el, key, (String(val)))));
|
||||
})(); }, keys(spreadAttrs(child))) : domAppend(el, child));
|
||||
})(); }, keys(spreadAttrs(child))) : domAppend(el, child)));
|
||||
})() : NIL), assoc(state, "i", (get(state, "i") + 1)))));
|
||||
})(); }, {["i"]: 0, ["skip"]: false}, args);
|
||||
return el;
|
||||
@@ -2269,6 +2269,44 @@ return effect(function() { return (function() {
|
||||
})();
|
||||
})(); }); };
|
||||
|
||||
// reactive-spread
|
||||
var reactiveSpread = function(el, renderFn) { return (function() {
|
||||
var prevClasses = [];
|
||||
var prevExtraKeys = [];
|
||||
(function() {
|
||||
var existing = sxOr(domGetAttr(el, "data-sx-reactive-attrs"), "");
|
||||
return domSetAttr(el, "data-sx-reactive-attrs", (isSxTruthy(isEmpty(existing)) ? "_spread" : (String(existing) + String(",_spread"))));
|
||||
})();
|
||||
return effect(function() { if (isSxTruthy(!isSxTruthy(isEmpty(prevClasses)))) {
|
||||
(function() {
|
||||
var current = sxOr(domGetAttr(el, "class"), "");
|
||||
var tokens = filter(function(c) { return !isSxTruthy((c == "")); }, split(current, " "));
|
||||
var kept = filter(function(c) { return !isSxTruthy(some(function(pc) { return (pc == c); }, prevClasses)); }, tokens);
|
||||
return (isSxTruthy(isEmpty(kept)) ? domRemoveAttr(el, "class") : domSetAttr(el, "class", join(" ", kept)));
|
||||
})();
|
||||
}
|
||||
{ var _c = prevExtraKeys; for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; domRemoveAttr(el, k); } }
|
||||
return (function() {
|
||||
var result = renderFn();
|
||||
return (isSxTruthy(isSpread(result)) ? (function() {
|
||||
var attrs = spreadAttrs(result);
|
||||
var clsStr = sxOr(dictGet(attrs, "class"), "");
|
||||
var newClasses = filter(function(c) { return !isSxTruthy((c == "")); }, split(clsStr, " "));
|
||||
var extraKeys = filter(function(k) { return !isSxTruthy((k == "class")); }, keys(attrs));
|
||||
prevClasses = newClasses;
|
||||
prevExtraKeys = extraKeys;
|
||||
if (isSxTruthy(!isSxTruthy(isEmpty(newClasses)))) {
|
||||
(function() {
|
||||
var current = sxOr(domGetAttr(el, "class"), "");
|
||||
return domSetAttr(el, "class", (isSxTruthy((isSxTruthy(current) && !isSxTruthy((current == "")))) ? (String(current) + String(" ") + String(clsStr)) : clsStr));
|
||||
})();
|
||||
}
|
||||
{ var _c = extraKeys; for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; domSetAttr(el, k, (String(dictGet(attrs, k)))); } }
|
||||
return flushCssxToDom();
|
||||
})() : ((prevClasses = []), (prevExtraKeys = [])));
|
||||
})(); });
|
||||
})(); };
|
||||
|
||||
// reactive-fragment
|
||||
var reactiveFragment = function(testFn, renderFn, env, ns) { return (function() {
|
||||
var marker = createComment("island-fragment");
|
||||
|
||||
@@ -232,30 +232,35 @@
|
||||
(do
|
||||
(when (not (contains? VOID_ELEMENTS tag))
|
||||
(let ((child (render-to-dom arg env new-ns)))
|
||||
(if (spread? child)
|
||||
;; Spread: merge attrs onto parent element
|
||||
(for-each
|
||||
(fn ((key :as string))
|
||||
(let ((val (dict-get (spread-attrs child) key)))
|
||||
(if (= key "class")
|
||||
;; Class: append to existing
|
||||
(let ((existing (dom-get-attr el "class")))
|
||||
(dom-set-attr el "class"
|
||||
(if (and existing (not (= existing "")))
|
||||
(str existing " " val)
|
||||
val)))
|
||||
(if (= key "style")
|
||||
;; Style: append with semicolon
|
||||
(let ((existing (dom-get-attr el "style")))
|
||||
(dom-set-attr el "style"
|
||||
(cond
|
||||
;; Reactive spread: track signal deps, update attrs on change
|
||||
(and (spread? child) *island-scope*)
|
||||
(reactive-spread el (fn () (render-to-dom arg env new-ns)))
|
||||
;; Static spread: one-shot merge attrs onto parent element
|
||||
(spread? child)
|
||||
(for-each
|
||||
(fn ((key :as string))
|
||||
(let ((val (dict-get (spread-attrs child) key)))
|
||||
(if (= key "class")
|
||||
;; Class: append to existing
|
||||
(let ((existing (dom-get-attr el "class")))
|
||||
(dom-set-attr el "class"
|
||||
(if (and existing (not (= existing "")))
|
||||
(str existing ";" val)
|
||||
(str existing " " val)
|
||||
val)))
|
||||
;; Other attrs: overwrite
|
||||
(dom-set-attr el key (str val))))))
|
||||
(keys (spread-attrs child)))
|
||||
(if (= key "style")
|
||||
;; Style: append with semicolon
|
||||
(let ((existing (dom-get-attr el "style")))
|
||||
(dom-set-attr el "style"
|
||||
(if (and existing (not (= existing "")))
|
||||
(str existing ";" val)
|
||||
val)))
|
||||
;; Other attrs: overwrite
|
||||
(dom-set-attr el key (str val))))))
|
||||
(keys (spread-attrs child)))
|
||||
;; Normal child: append to element
|
||||
(dom-append el child))))
|
||||
:else
|
||||
(dom-append el child))))
|
||||
(assoc state "i" (inc (get state "i"))))))))
|
||||
(dict "i" 0 "skip" false)
|
||||
args)
|
||||
@@ -867,6 +872,64 @@
|
||||
:else
|
||||
(dom-set-attr el attr-name (str val)))))))))
|
||||
|
||||
;; reactive-spread — reactively bind spread attrs to parent element.
|
||||
;; Used when a child of an element produces a spread inside an island.
|
||||
;; Tracks signal deps in the spread expression. When signals change:
|
||||
;; old classes are removed, new ones applied. Non-class attrs (data-tw etc.)
|
||||
;; are overwritten. Flushes newly collected CSS rules to live stylesheet.
|
||||
;;
|
||||
;; Multiple reactive spreads on the same element are safe — each tracks
|
||||
;; its own class contribution and only removes/adds its own tokens.
|
||||
(define reactive-spread :effects [render mutation]
|
||||
(fn (el (render-fn :as lambda))
|
||||
(let ((prev-classes (list))
|
||||
(prev-extra-keys (list)))
|
||||
;; Mark for morph protection
|
||||
(let ((existing (or (dom-get-attr el "data-sx-reactive-attrs") "")))
|
||||
(dom-set-attr el "data-sx-reactive-attrs"
|
||||
(if (empty? existing) "_spread" (str existing ",_spread"))))
|
||||
(effect (fn ()
|
||||
;; 1. Remove previously applied classes from element's class list
|
||||
(when (not (empty? prev-classes))
|
||||
(let ((current (or (dom-get-attr el "class") ""))
|
||||
(tokens (filter (fn (c) (not (= c ""))) (split current " ")))
|
||||
(kept (filter (fn (c)
|
||||
(not (some (fn (pc) (= pc c)) prev-classes)))
|
||||
tokens)))
|
||||
(if (empty? kept)
|
||||
(dom-remove-attr el "class")
|
||||
(dom-set-attr el "class" (join " " kept)))))
|
||||
;; 2. Remove previously applied extra attrs
|
||||
(for-each (fn (k) (dom-remove-attr el k)) prev-extra-keys)
|
||||
;; 3. Re-evaluate the spread expression (tracks signal deps)
|
||||
(let ((result (render-fn)))
|
||||
(if (spread? result)
|
||||
(let ((attrs (spread-attrs result))
|
||||
(cls-str (or (dict-get attrs "class") ""))
|
||||
(new-classes (filter (fn (c) (not (= c "")))
|
||||
(split cls-str " ")))
|
||||
(extra-keys (filter (fn (k) (not (= k "class")))
|
||||
(keys attrs))))
|
||||
(set! prev-classes new-classes)
|
||||
(set! prev-extra-keys extra-keys)
|
||||
;; Append new classes to element
|
||||
(when (not (empty? new-classes))
|
||||
(let ((current (or (dom-get-attr el "class") "")))
|
||||
(dom-set-attr el "class"
|
||||
(if (and current (not (= current "")))
|
||||
(str current " " cls-str)
|
||||
cls-str))))
|
||||
;; Set extra attrs (data-tw, etc.) — simple overwrite
|
||||
(for-each (fn (k)
|
||||
(dom-set-attr el k (str (dict-get attrs k))))
|
||||
extra-keys)
|
||||
;; Flush any newly collected CSS rules to live stylesheet
|
||||
(flush-cssx-to-dom))
|
||||
;; No longer a spread — clear tracked state
|
||||
(do
|
||||
(set! prev-classes (list))
|
||||
(set! prev-extra-keys (list))))))))))
|
||||
|
||||
;; reactive-fragment — conditionally render a fragment based on a signal
|
||||
;; Used for (when (deref sig) ...) or (if (deref sig) ...) inside an island.
|
||||
(define reactive-fragment :effects [render mutation]
|
||||
|
||||
Reference in New Issue
Block a user