Bump version to 1.2.10, simplify attribute and event handling in ElementExtensions, and remove deprecated logic in HtmlBuilder.
Some checks failed
Gradle CI / build (push) Has been cancelled

This commit is contained in:
2025-10-18 16:24:52 +02:00
parent d3fef98203
commit 88cf5b8533
7 changed files with 119 additions and 177 deletions

View File

@@ -11,7 +11,7 @@ plugins {
} }
group = "nl.astraeus" group = "nl.astraeus"
version = "1.2.9" version = "1.2.10"
repositories { repositories {
mavenCentral() mavenCentral()

View File

@@ -40,14 +40,6 @@ fun Element.printTree(indent: Int = 0): String {
} }
result.append(") {") result.append(") {")
result.append("\n") result.append("\n")
for ((name, event) in getKompEvents()) {
result.append(indent.asSpaces())
result.append("on")
result.append(name)
result.append(" -> ")
result.append(event)
result.append("\n")
}
for (index in 0 until childNodes.length) { for (index in 0 until childNodes.length) {
childNodes[index]?.let { childNodes[index]?.let {
if (it is Element) { if (it is Element) {
@@ -65,39 +57,21 @@ fun Element.printTree(indent: Int = 0): String {
return result.toString() return result.toString()
} }
internal fun Element.setKompAttribute(attributeName: String, value: String?) { internal fun Element.setKompAttribute(attributeName: String, value: String) {
//val attributeName = name.lowercase()
if (value == null || value.isBlank()) {
if (this is HTMLInputElement) {
when (attributeName) {
"checked" -> {
checked = false
}
"class" -> {
className = ""
}
"value" -> {
this.value = ""
}
else -> {
removeAttribute(attributeName)
}
}
} else {
removeAttribute(attributeName)
}
} else {
if (this is HTMLInputElement) { if (this is HTMLInputElement) {
when (attributeName) { when (attributeName) {
"checked" -> { "checked" -> {
checked = "checked" == value checked = "checked" == value
} }
"class" -> { "class" -> {
className = value className = value
} }
"value" -> { "value" -> {
this.value = value this.value = value
} }
else -> { else -> {
setAttribute(attributeName, value) setAttribute(attributeName, value)
} }
@@ -105,15 +79,30 @@ internal fun Element.setKompAttribute(attributeName: String, value: String?) {
} else if (this.getAttribute(attributeName) != value) { } else if (this.getAttribute(attributeName) != value) {
setAttribute(attributeName, value) setAttribute(attributeName, value)
} }
}
} }
internal fun Element.clearKompEvents() { internal fun Element.clearKompAttribute(attributeName: String) {
val events = getKompEvents() if (this is HTMLInputElement) {
for ((name, event) in getKompEvents()) { when (attributeName) {
removeEventListener(name, event) "checked" -> {
checked = false
}
"class" -> {
className = ""
}
"value" -> {
this.value = ""
}
else -> {
removeAttribute(attributeName)
}
}
} else {
removeAttribute(attributeName)
} }
events.clear()
} }
internal fun Element.setKompEvent(name: String, event: (Event) -> Unit) { internal fun Element.setKompEvent(name: String, event: (Event) -> Unit) {
@@ -123,22 +112,9 @@ internal fun Element.setKompEvent(name: String, event: (Event) -> Unit) {
name name
} }
getKompEvents()[eventName] = event
this.addEventListener(eventName, event) this.addEventListener(eventName, event)
} }
internal fun Element.getKompEvents(): MutableMap<String, (Event) -> Unit> {
var map = this.asDynamic()["komp-events"] as? MutableMap<String, (Event) -> Unit>
if (map == null) {
map = mutableMapOf()
this.asDynamic()["komp-events"] = map
}
return map
}
internal fun Element.findElementIndex(): Int { internal fun Element.findElementIndex(): Int {
val childNodes = parentElement?.children val childNodes = parentElement?.children
if (childNodes != null) { if (childNodes != null) {

View File

@@ -6,7 +6,6 @@ import org.w3c.dom.get
data class ElementIndex( data class ElementIndex(
val parent: Node, val parent: Node,
var childIndex: Int, var childIndex: Int,
var setAttr: MutableSet<String> = mutableSetOf()
) { ) {
override fun toString(): String { override fun toString(): String {
return "${parent.nodeName}[$childIndex]" return "${parent.nodeName}[$childIndex]"
@@ -39,7 +38,6 @@ fun ArrayList<ElementIndex>.currentPosition(): ElementIndex? {
fun ArrayList<ElementIndex>.nextElement() { fun ArrayList<ElementIndex>.nextElement() {
this.lastOrNull()?.let { this.lastOrNull()?.let {
it.setAttr.clear()
it.childIndex++ it.childIndex++
} }
} }

View File

@@ -57,12 +57,6 @@ class HtmlBuilder(
} }
} else { } else {
// current element should become parent // current element should become parent
/*
val ce = komponent.element
if (ce != null) {
append(ce as Element)
}
*/
komponent.create( komponent.create(
currentPosition.last().parent as Element, currentPosition.last().parent as Element,
currentPosition.last().childIndex currentPosition.last().childIndex
@@ -100,25 +94,18 @@ class HtmlBuilder(
"onTagStart, [${tag.tagName}, ${tag.namespace ?: ""}], currentPosition: $currentPosition" "onTagStart, [${tag.tagName}, ${tag.namespace ?: ""}], currentPosition: $currentPosition"
} }
currentNode = currentPosition.currentElement()
if (currentNode == null) {
logReplace { "onTagStart, currentNode1: $currentNode" }
currentNode = if (tag.namespace != null) { currentNode = if (tag.namespace != null) {
document.createElementNS(tag.namespace, tag.tagName) document.createElementNS(tag.namespace, tag.tagName)
} else { } else {
document.createElement(tag.tagName) document.createElement(tag.tagName)
} }
if (currentNode == null) {
logReplace { "onTagStart, currentNode1: $currentNode" }
logReplace { "onTagStart, currentElement1.1: $currentNode" } logReplace { "onTagStart, currentElement1.1: $currentNode" }
currentPosition.currentParent().appendChild(currentNode!!) currentPosition.currentParent().appendChild(currentNode!!)
} else if ( } else {
!currentNode?.asElement()?.tagName.equals(tag.tagName, true) ||
(
tag.namespace != null &&
!currentNode?.asElement()?.namespaceURI.equals(tag.namespace, true)
)
) {
logReplace { logReplace {
"onTagStart, currentElement, namespace: ${currentNode?.asElement()?.namespaceURI} -> ${tag.namespace}" "onTagStart, currentElement, namespace: ${currentNode?.asElement()?.namespaceURI} -> ${tag.namespace}"
} }
@@ -126,12 +113,6 @@ class HtmlBuilder(
"onTagStart, currentElement, replace: ${currentNode?.asElement()?.tagName} -> ${tag.tagName}" "onTagStart, currentElement, replace: ${currentNode?.asElement()?.tagName} -> ${tag.tagName}"
} }
currentNode = if (tag.namespace != null) {
document.createElementNS(tag.namespace, tag.tagName)
} else {
document.createElement(tag.tagName)
}
currentPosition.replace(currentNode!!) currentPosition.replace(currentNode!!)
} }
@@ -144,15 +125,8 @@ class HtmlBuilder(
firstTag = false firstTag = false
} }
currentElement?.clearKompEvents()
// if currentElement = checkbox make sure it's cleared
(currentElement as? HTMLInputElement)?.checked = false
currentPosition.lastOrNull()?.setAttr?.clear()
for (entry in tag.attributesEntries) { for (entry in tag.attributesEntries) {
currentElement!!.setKompAttribute(entry.key, entry.value) currentElement?.setKompAttribute(entry.key, entry.value)
currentPosition.lastOrNull()?.setAttr?.add(entry.key)
} }
} }
@@ -181,11 +155,10 @@ class HtmlBuilder(
checkTag("onTagAttributeChange", tag) checkTag("onTagAttributeChange", tag)
} }
currentElement?.setKompAttribute(attribute, value)
if (value == null || value.isEmpty()) { if (value == null || value.isEmpty()) {
currentPosition.currentPosition()?.setAttr?.remove(attribute) currentElement?.clearKompAttribute(attribute)
} else { } else {
currentPosition.currentPosition()?.setAttr?.add(attribute) currentElement?.setKompAttribute(attribute, value)
} }
} }
@@ -222,25 +195,6 @@ class HtmlBuilder(
checkTag("onTagEnd", tag) checkTag("onTagEnd", tag)
} }
if (currentElement != null) {
val setAttrs: Set<String> = currentPosition.currentPosition()?.setAttr ?: setOf()
// remove attributes that where not set
val element = currentElement
if (element?.hasAttributes() == true) {
for (index in 0 until element.attributes.length) {
val attribute = element.attributes[index]
if (attribute?.name != null) {
val attr = attribute.name
if (!setAttrs.contains(attr)) {
element.setKompAttribute(attr, null)
}
}
}
}
}
currentPosition.pop() currentPosition.pop()
currentNode = currentPosition.currentElement() currentNode = currentPosition.currentElement()

View File

@@ -38,7 +38,7 @@ class TestClassUpdate {
Komponent.create(div, classComponent) Komponent.create(div, classComponent)
// Verify initial state - should have the class // Verify initial state - should have the class
val contentDiv = div.querySelector("div") var contentDiv = div.querySelector("div")
println("[DEBUG_LOG] Initial DOM: ${div.printTree()}") println("[DEBUG_LOG] Initial DOM: ${div.printTree()}")
assertTrue(contentDiv?.classList?.contains("test-class") ?: false, "Div should have the class initially") assertTrue(contentDiv?.classList?.contains("test-class") ?: false, "Div should have the class initially")
@@ -47,6 +47,7 @@ class TestClassUpdate {
classComponent.requestImmediateUpdate() classComponent.requestImmediateUpdate()
// Verify the class was removed // Verify the class was removed
contentDiv = div.querySelector("div")
println("[DEBUG_LOG] After class removal: ${div.printTree()}") println("[DEBUG_LOG] After class removal: ${div.printTree()}")
assertFalse(contentDiv?.classList?.contains("test-class") ?: true, "Class should be removed after update") assertFalse(contentDiv?.classList?.contains("test-class") ?: true, "Class should be removed after update")
@@ -55,6 +56,7 @@ class TestClassUpdate {
classComponent.requestImmediateUpdate() classComponent.requestImmediateUpdate()
// Verify the class was added back // Verify the class was added back
contentDiv = div.querySelector("div")
println("[DEBUG_LOG] After class added back: ${div.printTree()}") println("[DEBUG_LOG] After class added back: ${div.printTree()}")
assertTrue(contentDiv?.classList?.contains("test-class") ?: false, "Class should be added back") assertTrue(contentDiv?.classList?.contains("test-class") ?: false, "Class should be added back")
@@ -63,6 +65,7 @@ class TestClassUpdate {
classComponent.requestImmediateUpdate() classComponent.requestImmediateUpdate()
// Verify the class was changed // Verify the class was changed
contentDiv = div.querySelector("div")
println("[DEBUG_LOG] After class name change: ${div.printTree()}") println("[DEBUG_LOG] After class name change: ${div.printTree()}")
assertFalse(contentDiv?.classList?.contains("test-class") ?: true, "Old class should be removed") assertFalse(contentDiv?.classList?.contains("test-class") ?: true, "Old class should be removed")
assertTrue(contentDiv?.classList?.contains("new-class") ?: false, "New class should be added") assertTrue(contentDiv?.classList?.contains("new-class") ?: false, "New class should be added")

View File

@@ -77,9 +77,9 @@ class TestInsert {
tableComponent.addRow("First Row") tableComponent.addRow("First Row")
// Verify the row was added // Verify the row was added
val rowsAfterFirstInsert = table.querySelectorAll("tr") val rowsAfterFirstInsert = div.querySelector("table")?.querySelectorAll("tr")
assertEquals(1, rowsAfterFirstInsert.length, "Table should have one row after insertion") assertEquals(1, rowsAfterFirstInsert?.length, "Table should have one row after insertion")
val firstRowCell = table.querySelector("tr td") val firstRowCell = div.querySelector("table")?.querySelector("tr td")
assertNotNull(firstRowCell, "First row cell should exist") assertNotNull(firstRowCell, "First row cell should exist")
assertEquals("First Row", firstRowCell.textContent, "Row content should match") assertEquals("First Row", firstRowCell.textContent, "Row content should match")
@@ -87,12 +87,12 @@ class TestInsert {
tableComponent.addRow("Second Row") tableComponent.addRow("Second Row")
// Verify both rows are present // Verify both rows are present
val rowsAfterSecondInsert = table.querySelectorAll("tr") val rowsAfterSecondInsert = div.querySelector("table")?.querySelectorAll("tr")
assertEquals(2, rowsAfterSecondInsert.length, "Table should have two rows after second insertion") assertEquals(2, rowsAfterSecondInsert?.length, "Table should have two rows after second insertion")
val allCells = table.querySelectorAll("tr td") val allCells = div.querySelector("table")?.querySelectorAll("tr td")
assertEquals(2, allCells.length, "Table should have two cells") assertEquals(2, allCells?.length, "Table should have two cells")
assertEquals("First Row", allCells.item(0)?.textContent, "First row content should match") assertEquals("First Row", allCells?.item(0)?.textContent, "First row content should match")
assertEquals("Second Row", allCells.item(1)?.textContent, "Second row content should match") assertEquals("Second Row", allCells?.item(1)?.textContent, "Second row content should match")
// Print the DOM tree for debugging // Print the DOM tree for debugging
println("Table DOM: ${div.printTree()}") println("Table DOM: ${div.printTree()}")

View File

@@ -37,15 +37,20 @@ class TestStyleUpdate {
Komponent.create(div, styleComponent) Komponent.create(div, styleComponent)
// Verify initial state - should have the style // Verify initial state - should have the style
val contentDiv = div.querySelector("div") var contentDiv = div.querySelector("div")
println("[DEBUG_LOG] Initial DOM: ${div.printTree()}") println("[DEBUG_LOG] Initial DOM: ${div.printTree()}")
assertEquals("color: red;", contentDiv?.getAttribute("style"), "Div should have the style initially") assertEquals(
"color: red;",
contentDiv?.getAttribute("style"),
"Div should have the style initially"
)
// Update to remove the style // Update to remove the style
styleComponent.includeStyle = false styleComponent.includeStyle = false
styleComponent.requestImmediateUpdate() styleComponent.requestImmediateUpdate()
// Verify the style was removed // Verify the style was removed
contentDiv = div.querySelector("div")
println("[DEBUG_LOG] After style removal: ${div.printTree()}") println("[DEBUG_LOG] After style removal: ${div.printTree()}")
assertNull(contentDiv?.getAttribute("style"), "Style should be removed after update") assertNull(contentDiv?.getAttribute("style"), "Style should be removed after update")
@@ -54,6 +59,7 @@ class TestStyleUpdate {
styleComponent.requestImmediateUpdate() styleComponent.requestImmediateUpdate()
// Verify the style was added back // Verify the style was added back
contentDiv = div.querySelector("div")
println("[DEBUG_LOG] After style added back: ${div.printTree()}") println("[DEBUG_LOG] After style added back: ${div.printTree()}")
assertEquals("color: red;", contentDiv?.getAttribute("style"), "Style should be added back") assertEquals("color: red;", contentDiv?.getAttribute("style"), "Style should be added back")
@@ -62,7 +68,12 @@ class TestStyleUpdate {
styleComponent.requestImmediateUpdate() styleComponent.requestImmediateUpdate()
// Verify the style was changed // Verify the style was changed
contentDiv = div.querySelector("div")
println("[DEBUG_LOG] After style value change: ${div.printTree()}") println("[DEBUG_LOG] After style value change: ${div.printTree()}")
assertEquals("color: blue;", contentDiv?.getAttribute("style"), "Style should be updated to new value") assertEquals(
"color: blue;",
contentDiv?.getAttribute("style"),
"Style should be updated to new value"
)
} }
} }