Implement reactive attributes for minimal DOM updates
Features: - Track attributes that depend on signals (ternary expressions) - Store element references in globals for reactive attributes - Re-evaluate and update only changed attributes when signals change - Similar to reactive conditionals but for attributes Performance: - Minimal DOM updates - only changed attributes are updated - No full re-renders, no virtual DOM diffing - Direct attribute updates via js_set_attribute Example: class_='tab active' if tab_index == 0 else 'tab' When tab_index changes, only the class attribute updates. Implementation: - Collection phase: scan for attributes with signal dependencies - Render phase: store element references to globals - Signal update: re-evaluate expression and update attribute All tests passing (55 passed, 2 skipped) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
b0e38db22c
commit
1575571f3e
6 files changed
+761
-474
No files matched your search
Binary file not shown.
@@ -0,0 +1,99 @@
|
||||
"""Tests for reactive attributes that update when signals change."""
|
||||
|
||||
import pytest
|
||||
from ritz.parser import parse_component
|
||||
from ritz.llvm_codegen_signals import generate_llvm_ir
|
||||
|
||||
|
||||
def test_parse_reactive_attribute():
|
||||
"""Test parsing attribute with signal dependency."""
|
||||
source = """
|
||||
from ritz.syntax import *
|
||||
|
||||
@component
|
||||
def Toggle():
|
||||
active = signal(1)
|
||||
|
||||
with Button(class_='active' if active else 'inactive'):
|
||||
yield "Click"
|
||||
"""
|
||||
ir = parse_component(source)
|
||||
|
||||
# Should have ternary expression in class attribute
|
||||
with_stmt = [s for s in ir["body"] if s["type"] == "with"][0]
|
||||
class_prop = with_stmt["element"]["props"]["class_"]
|
||||
assert class_prop["type"] == "ifexp"
|
||||
assert class_prop["test"]["id"] == "active"
|
||||
|
||||
|
||||
def test_reactive_attribute_infrastructure():
|
||||
"""Test that reactive attribute tracking is generated."""
|
||||
source = """
|
||||
from ritz.syntax import *
|
||||
|
||||
@component
|
||||
def Toggle():
|
||||
active = signal(1)
|
||||
|
||||
def toggle():
|
||||
active = 0 if active else 1
|
||||
|
||||
with Button(class_='btn-active' if active else 'btn-inactive', onclick=toggle):
|
||||
yield "Toggle"
|
||||
"""
|
||||
ir = parse_component(source)
|
||||
llvm_ir = generate_llvm_ir(ir)
|
||||
|
||||
# Should have storage for the button element
|
||||
assert "@reactive_attr_" in llvm_ir or "@elem_" in llvm_ir
|
||||
|
||||
# Should update attribute when signal changes in toggle()
|
||||
# The toggle function should re-evaluate the class expression and update
|
||||
assert "js_set_attribute" in llvm_ir
|
||||
|
||||
|
||||
def test_multiple_reactive_attributes():
|
||||
"""Test multiple elements with reactive attributes."""
|
||||
source = """
|
||||
from ritz.syntax import *
|
||||
|
||||
@component
|
||||
def Tabs():
|
||||
tab = signal(0)
|
||||
|
||||
def set_tab(value):
|
||||
tab = value
|
||||
|
||||
with Ul():
|
||||
with Li(class_='active' if tab == 0 else ''):
|
||||
yield "Tab 0"
|
||||
|
||||
with Li(class_='active' if tab == 1 else ''):
|
||||
yield "Tab 1"
|
||||
"""
|
||||
ir = parse_component(source)
|
||||
llvm_ir = generate_llvm_ir(ir)
|
||||
|
||||
# Both Li elements should have reactive attributes
|
||||
# When tab signal changes, both should update
|
||||
assert llvm_ir.count("js_set_attribute") >= 2
|
||||
|
||||
|
||||
def test_reactive_attribute_with_comparison():
|
||||
"""Test reactive attribute with comparison in ternary."""
|
||||
source = """
|
||||
from ritz.syntax import *
|
||||
|
||||
@component
|
||||
def Counter():
|
||||
count = signal(5)
|
||||
|
||||
with Div(class_='high' if count > 10 else 'low'):
|
||||
yield "Value"
|
||||
"""
|
||||
ir = parse_component(source)
|
||||
llvm_ir = generate_llvm_ir(ir)
|
||||
|
||||
# Should emit comparison when evaluating class
|
||||
assert "icmp" in llvm_ir
|
||||
assert "select" in llvm_ir
|
||||
Reference in new issue
Block a user