Working diff option

This commit is contained in:
2020-05-05 21:12:41 +02:00
parent 7677cbcc7c
commit 70723920b3
2 changed files with 234 additions and 183 deletions

View File

@@ -2,23 +2,52 @@ package nl.astraeus.komp
import org.w3c.dom.HTMLElement
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import org.w3c.dom.events.Event
import org.w3c.dom.get
object DiffPatch {
fun updateNode(oldNode: Node, newNode: Node): Node {
if (oldNode is HTMLElement && newNode is HTMLElement) {
if (oldNode.nodeName == newNode.nodeName) {
if (oldNode.getAttribute("data-komp-hash") != null &&
oldNode.getAttribute("data-komp-hash") == newNode.getAttribute("data-komp-hash")) {
if (Komponent.logReplaceEvent) {
console.log("Skip node, hash equals", oldNode, newNode)
private fun NodeList.findNodeWithHash(hash: String): HTMLElement? {
for (index in 0..this.length) {
val node = this[index]
if (node is HTMLElement && node.getAttribute(DiffPatch.HASH_ATTRIBUTE) == hash) {
return node
}
}
return null
}
object DiffPatch {
const val HASH_ATTRIBUTE = "data-komp-hash"
fun hashesMatch(oldNode: Node, newNode: Node): Boolean {
return (
oldNode is HTMLElement &&
newNode is HTMLElement &&
oldNode.nodeName == newNode.nodeName &&
oldNode.getAttribute(HASH_ATTRIBUTE) != null &&
oldNode.getAttribute(HASH_ATTRIBUTE) == newNode.getAttribute(HASH_ATTRIBUTE)
)
}
fun updateNode(oldNode: Node, newNode: Node): Node {
if (hashesMatch(oldNode, newNode)) {
return oldNode
} else {
}
if (oldNode.nodeType == newNode.nodeType && oldNode.nodeType == 3.toShort()) {
if (oldNode.textContent != newNode.textContent) {
if (Komponent.logReplaceEvent) {
console.log("Updating text content", oldNode, newNode)
}
oldNode.textContent = newNode.textContent
return oldNode
}
}
if (oldNode is HTMLElement && newNode is HTMLElement) {
if (oldNode.nodeName == newNode.nodeName) {
if (Komponent.logReplaceEvent) {
console.log("Update attributes", oldNode.innerHTML, newNode.innerHTML)
}
@@ -30,22 +59,6 @@ object DiffPatch {
updateEvents(oldNode, newNode)
return oldNode
}
} else {
if (Komponent.logReplaceEvent) {
console.log("Replace node ee", oldNode.innerHTML, newNode.innerHTML)
}
replaceNode(oldNode, newNode)
return newNode
}
} else {
if (oldNode.nodeType == newNode.nodeType && oldNode.nodeType == 3.toShort()) {
if (oldNode.textContent != newNode.textContent) {
if (Komponent.logReplaceEvent) {
console.log("Updating text content", oldNode, newNode)
}
oldNode.textContent = newNode.textContent
return oldNode
}
}
if (Komponent.logReplaceEvent) {
@@ -53,8 +66,6 @@ object DiffPatch {
}
replaceNode(oldNode, newNode)
return newNode
}
}
private fun updateAttributes(oldNode: HTMLElement, newNode: HTMLElement) {
@@ -81,7 +92,6 @@ object DiffPatch {
}
private fun updateChildren(oldNode: HTMLElement, newNode: HTMLElement) {
// todo: add 1 look ahead/back
var oldIndex = 0
var newIndex = 0
@@ -99,7 +109,32 @@ object DiffPatch {
if (oldIndex < oldNode.childNodes.length) {
val oldChildNode = oldNode.childNodes[oldIndex]
if (oldChildNode != null && newChildNode != null) {
if (!hashesMatch(oldChildNode, newChildNode) && newChildNode is HTMLElement && oldChildNode is HTMLElement) {
val oldHash = oldChildNode.getAttribute("data-komp-hash")
val newHash = newChildNode.getAttribute("data-komp-hash")
if (oldHash != null) {
val nodeWithHash = oldNode.childNodes.findNodeWithHash(oldHash)
if (nodeWithHash != null) {
if (Komponent.logReplaceEvent) {
console.log(">-> swap nodes", oldNode)
}
oldNode.replaceChild(oldChildNode, nodeWithHash)
oldNode.insertBefore(nodeWithHash, oldNode.childNodes[oldIndex])
if (Komponent.logReplaceEvent) {
console.log(">-> swapped nodes", oldNode)
}
}
} else if (newHash != null) {
// if node found after current new index, insert new node
}
}
if (Komponent.logReplaceEvent) {
console.log("Update node Old/new", oldChildNode, newChildNode)
}

View File

@@ -8,14 +8,27 @@ import kotlin.browser.document
@Suppress("NOTHING_TO_INLINE")
private inline fun HTMLElement.setEvent(name: String, noinline callback: (Event) -> Unit): Unit {
val eventName = if (name.startsWith("on")) { name.substring(2) } else { name }
val eventName = if (name.startsWith("on")) {
name.substring(2)
} else {
name
}
addEventListener(eventName, callback, null)
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
//asDynamic()[name] = callback
val events = getAttribute("data-komp-events") ?: ""
setAttribute("data-komp-events", if (events.isBlank()) { eventName } else { "$events,$eventName" })
setAttribute(
"data-komp-events",
if (events.isBlank()) {
eventName
} else {
"$events,$eventName"
}
)
asDynamic()["event-$eventName"] = callback
}
}
interface HtmlConsumer : TagConsumer<HTMLElement> {
fun append(node: Node)
@@ -72,27 +85,29 @@ class HtmlBuilder(
}
override fun onTagEnd(tag: Tag) {
var hash = 0
var hash: UInt = 0.toUInt()
if (path.isEmpty() || path.last().tagName.toLowerCase() != tag.tagName.toLowerCase()) {
throw IllegalStateException("We haven't entered tag ${tag.tagName} but trying to leave")
}
val element = path.last()
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
for (index in 0 until element.childNodes.length) {
val child = element.childNodes[index]
if (child is HTMLElement) {
hash = hash * 37 + (child.getAttribute("data-komp-hash")?.toInt() ?: 0)
hash = hash * 37.toUInt() + (child.getAttribute(DiffPatch.HASH_ATTRIBUTE)?.toUInt(16) ?: 0.toUInt())
} else {
hash = hash * 37 + (child?.textContent?.hashCode() ?: 0)
hash = hash * 37.toUInt() + (child?.textContent?.hashCode()?.toUInt() ?: 0.toUInt())
}
}
}
tag.attributesEntries.forEach {
hash = hash * 37 + it.key.hashCode()
hash = hash * 37 + it.value.hashCode()
val key_value = "${it.key}-${it.value}"
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
hash = hash * 37.toUInt() + key_value.hashCode().toUInt()
}
if (it.key == "class") {
val classes = it.value.split(Regex("\\s+"))
val classNames = StringBuilder()
@@ -134,8 +149,9 @@ class HtmlBuilder(
}
}
element.setAttribute("data-komp-hash", hash.toString())
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
element.setAttribute(DiffPatch.HASH_ATTRIBUTE, hash.toString(16))
}
lastLeaved = path.removeAt(path.lastIndex)
}