Fix events, fix per komponent hash
This commit is contained in:
@@ -6,7 +6,7 @@ import org.w3c.dom.events.Event
|
||||
const val HASH_VALUE = "komp-hash-value"
|
||||
|
||||
//const val HASH_ATTRIBUTE = "data-komp-hash"
|
||||
const val EVENT_ATTRIBUTE = "data-komp-events"
|
||||
const val EVENT_PROPERTY = "komp-events"
|
||||
|
||||
fun Node.getKompHash(): Int = this.asDynamic()[HASH_VALUE] as? Int? ?: -1
|
||||
|
||||
@@ -32,6 +32,8 @@ object DiffPatch {
|
||||
oldNode is HTMLElement &&
|
||||
newNode is HTMLElement &&
|
||||
oldNode.nodeName == newNode.nodeName &&
|
||||
oldNode.getKompHash() != -1 &&
|
||||
newNode.getKompHash() != -1 &&
|
||||
oldNode.getKompHash() == newNode.getKompHash()
|
||||
)
|
||||
}
|
||||
@@ -81,10 +83,10 @@ object DiffPatch {
|
||||
if (Komponent.logReplaceEvent) {
|
||||
console.log("Update children", oldNode.nodeName, newNode.nodeName)
|
||||
}
|
||||
updateKomponentOnNode(oldNode, newNode)
|
||||
updateChildren(oldNode, newNode)
|
||||
oldNode.setKompHash(newNode.getKompHash())
|
||||
|
||||
updateKomponentOnNode(oldNode, newNode)
|
||||
return oldNode
|
||||
}
|
||||
}
|
||||
@@ -94,7 +96,6 @@ object DiffPatch {
|
||||
}
|
||||
|
||||
oldNode.parentNode?.replaceChild(newNode, oldNode)
|
||||
//replaceNode(oldNode, newNode)
|
||||
return newNode
|
||||
}
|
||||
|
||||
@@ -123,21 +124,8 @@ object DiffPatch {
|
||||
|
||||
if (newNode is HTMLInputElement && oldNode is HTMLInputElement) {
|
||||
oldNode.value = newNode.value
|
||||
oldNode.checked = newNode.checked
|
||||
}
|
||||
|
||||
/*
|
||||
for (index in 0 until newNode.attributes.length) {
|
||||
val attr = newNode.attributes[index]
|
||||
|
||||
if (attr != null) {
|
||||
val oldAttr = oldNode.attributes[attr.name]
|
||||
|
||||
if (oldAttr == null || oldAttr.value != attr.value) {
|
||||
oldNode.setAttribute(attr.name, attr.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private fun updateChildren(oldNode: HTMLElement, newNode: HTMLElement) {
|
||||
@@ -278,86 +266,32 @@ object DiffPatch {
|
||||
}
|
||||
|
||||
private fun updateEvents(oldNode: HTMLElement, newNode: HTMLElement) {
|
||||
val oldEvents = mutableListOf<String>()
|
||||
oldEvents.addAll((oldNode.getAttribute(EVENT_ATTRIBUTE) ?: "").split(","))
|
||||
|
||||
val newEvents = (newNode.getAttribute(EVENT_ATTRIBUTE) ?: "").split(",")
|
||||
val oldEvents = (oldNode.asDynamic()[EVENT_PROPERTY] as? MutableList<String>) ?: mutableListOf()
|
||||
val newEvents = (newNode.asDynamic()[EVENT_PROPERTY] as? MutableList<String>) ?: mutableListOf()
|
||||
|
||||
if (Komponent.logReplaceEvent) {
|
||||
console.log("Update events", oldNode.getAttribute(EVENT_ATTRIBUTE), newNode.getAttribute(EVENT_ATTRIBUTE))
|
||||
}
|
||||
|
||||
for (event in newEvents) {
|
||||
if (event.isNotBlank()) {
|
||||
val oldNodeEvent = oldNode.asDynamic()["event-$event"]
|
||||
val newNodeEvent = newNode.asDynamic()["event-$event"]
|
||||
if (oldNodeEvent != null) {
|
||||
if (Komponent.logReplaceEvent) {
|
||||
console.log("Remove old event $event")
|
||||
}
|
||||
oldNode.removeEventListener(event, oldNodeEvent as ((Event) -> Unit), null)
|
||||
}
|
||||
if (newNodeEvent != null) {
|
||||
if (Komponent.logReplaceEvent) {
|
||||
console.log("Set event $event on", oldNode)
|
||||
}
|
||||
oldNode.setEvent(event, newNodeEvent as ((Event) -> Unit))
|
||||
}
|
||||
oldEvents.remove(event)
|
||||
}
|
||||
console.log("Update events", oldNode.getAttribute(EVENT_PROPERTY), newNode.getAttribute(EVENT_PROPERTY))
|
||||
}
|
||||
|
||||
for (event in oldEvents) {
|
||||
if (event.isNotBlank()) {
|
||||
val oldNodeEvent = oldNode.asDynamic()["event-$event"]
|
||||
if (oldNodeEvent != null) {
|
||||
oldNode.removeEventListener(event, oldNodeEvent as ((Event) -> Unit), null)
|
||||
oldNode.removeKompEvent(event)
|
||||
}
|
||||
|
||||
for (event in newEvents) {
|
||||
val newNodeEvent = newNode.asDynamic()["event-$event"]
|
||||
|
||||
if (newNodeEvent != null) {
|
||||
if (Komponent.logReplaceEvent) {
|
||||
console.log("Set event $event on", oldNode)
|
||||
}
|
||||
oldNode.setKompEvent(event, newNodeEvent as ((Event) -> Unit))
|
||||
}
|
||||
}
|
||||
|
||||
newNode.getAttribute(EVENT_ATTRIBUTE)?.also {
|
||||
oldNode.setAttribute(EVENT_ATTRIBUTE, it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun replaceNode(oldNode: Node, newNode: Node) {
|
||||
oldNode.parentNode?.also { parent ->
|
||||
val clone = newNode.cloneNode(true)
|
||||
cloneEvents(clone, newNode)
|
||||
parent.replaceChild(clone, oldNode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun cloneEvents(destination: Node, source: Node) {
|
||||
if (source is HTMLElement && destination is HTMLElement) {
|
||||
val events = (source.getAttribute(EVENT_ATTRIBUTE) ?: "").split(",")
|
||||
for (event in events) {
|
||||
if (event.isNotBlank()) {
|
||||
if (Komponent.logReplaceEvent) {
|
||||
console.log("Clone event $event on", source)
|
||||
}
|
||||
|
||||
val foundEvent = source.asDynamic()["event-$event"]
|
||||
if (foundEvent != null) {
|
||||
if (Komponent.logReplaceEvent) {
|
||||
console.log("Clone add eventlistener", foundEvent)
|
||||
}
|
||||
destination.setEvent(event, foundEvent as ((Event) -> Unit))
|
||||
} else {
|
||||
if (Komponent.logReplaceEvent) {
|
||||
console.log("Event not found $event", source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (index in 0 until source.childNodes.length) {
|
||||
destination.childNodes[index]?.also { destinationChild ->
|
||||
source.childNodes[index]?.also { sourceChild ->
|
||||
cloneEvents(destinationChild, sourceChild)
|
||||
}
|
||||
}
|
||||
if (newEvents.isEmpty()) {
|
||||
oldNode.asDynamic()[EVENT_PROPERTY] = null
|
||||
} else {
|
||||
oldNode.asDynamic()[EVENT_PROPERTY] = newNode.asDynamic()[EVENT_PROPERTY]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,29 +7,57 @@ import org.w3c.dom.css.CSSStyleDeclaration
|
||||
import org.w3c.dom.events.Event
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun HTMLElement.setEvent(name: String, noinline callback: (Event) -> Unit) {
|
||||
inline fun HTMLElement.setKompEvent(name: String, noinline callback: (Event) -> Unit) {
|
||||
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(EVENT_ATTRIBUTE) ?: ""
|
||||
val events: MutableList<String> = (asDynamic()[EVENT_PROPERTY] as? MutableList<String>) ?: mutableListOf()
|
||||
|
||||
setAttribute(
|
||||
EVENT_ATTRIBUTE,
|
||||
if (events.isBlank()) {
|
||||
eventName
|
||||
} else {
|
||||
"$events,$eventName"
|
||||
}
|
||||
)
|
||||
events.add(eventName)
|
||||
asDynamic()[EVENT_PROPERTY] = events
|
||||
asDynamic()["event-$eventName"] = callback
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun HTMLElement.removeKompEvent(name: String) {
|
||||
val eventName = if (name.startsWith("on")) {
|
||||
name.substring(2)
|
||||
} else {
|
||||
name
|
||||
}
|
||||
|
||||
removeEventListener(eventName, asDynamic()["event-$eventName"] as ((Event) -> Unit), null)
|
||||
|
||||
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||
//asDynamic()[name] = callback
|
||||
val events: MutableList<String> = (asDynamic()[EVENT_PROPERTY] as? MutableList<String>) ?: mutableListOf()
|
||||
|
||||
events.remove(eventName)
|
||||
asDynamic()["event-$eventName"] = null
|
||||
}
|
||||
}
|
||||
|
||||
fun HTMLElement.clearEvents() {
|
||||
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||
//asDynamic()[name] = callback
|
||||
val events = getAttribute(EVENT_PROPERTY) ?: ""
|
||||
|
||||
for (eventName in events.split(",")) {
|
||||
if (eventName.isNotBlank()) {
|
||||
val event: (Event) -> Unit = asDynamic()["event-$eventName"]
|
||||
removeEventListener(eventName, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface HtmlConsumer : TagConsumer<HTMLElement> {
|
||||
fun append(node: Node)
|
||||
}
|
||||
@@ -43,8 +71,9 @@ fun HTMLElement.setStyles(cssStyle: CSSStyleDeclaration) {
|
||||
}
|
||||
|
||||
class HtmlBuilder(
|
||||
val komponent: Komponent,
|
||||
val document: Document
|
||||
val komponent: Komponent,
|
||||
val document: Document,
|
||||
val baseHash: Int
|
||||
) : HtmlConsumer {
|
||||
private val path = arrayListOf<HTMLElement>()
|
||||
private var lastLeaved: HTMLElement? = null
|
||||
@@ -80,7 +109,7 @@ class HtmlBuilder(
|
||||
when {
|
||||
path.isEmpty() -> throw IllegalStateException("No current tag")
|
||||
path.last().tagName.toLowerCase() != tag.tagName.toLowerCase() -> throw IllegalStateException("Wrong current tag")
|
||||
else -> path.last().setEvent(event, value)
|
||||
else -> path.last().setKompEvent(event, value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,13 +190,10 @@ class HtmlBuilder(
|
||||
hash = hash * 37 + key_value.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (Komponent.updateStrategy == UpdateStrategy.DOM_DIFF) {
|
||||
element.setKompHash(hash)
|
||||
//element.setAttribute(DiffPatch.HASH_ATTRIBUTE, hash.toString(16))
|
||||
element.setKompHash(baseHash * 53 + hash)
|
||||
}
|
||||
lastLeaved = path.removeAt(path.lastIndex)
|
||||
}
|
||||
@@ -221,7 +247,8 @@ class HtmlBuilder(
|
||||
|
||||
companion object {
|
||||
fun create(content: HtmlBuilder.() -> Unit): HTMLElement {
|
||||
val consumer = HtmlBuilder(DummyKomponent(), document)
|
||||
val komponent = DummyKomponent()
|
||||
val consumer = HtmlBuilder(komponent, document, komponent.hashCode())
|
||||
content.invoke(consumer)
|
||||
return consumer.finalize()
|
||||
}
|
||||
|
||||
@@ -70,8 +70,14 @@ abstract class Komponent {
|
||||
val declaredStyles: MutableMap<String, CSSStyleDeclaration> = HashMap()
|
||||
|
||||
open fun create(): HTMLElement {
|
||||
val consumer = HtmlBuilder(this, document)
|
||||
consumer.render()
|
||||
val consumer = HtmlBuilder(this, document, this.createIndex)
|
||||
try {
|
||||
consumer.render()
|
||||
} catch (e: Throwable) {
|
||||
println("Exception occurred in ${this::class.simpleName}.render() call!")
|
||||
|
||||
throw e
|
||||
}
|
||||
val result = consumer.finalize()
|
||||
|
||||
if (result.id.isBlank()) {
|
||||
@@ -188,9 +194,8 @@ abstract class Komponent {
|
||||
|
||||
todo.forEach { next ->
|
||||
val element = next.element
|
||||
console.log("update element", element)
|
||||
|
||||
if (element is HTMLElement) {
|
||||
console.log("by id", document.getElementById(element.id))
|
||||
if (document.getElementById(element.id) != null) {
|
||||
if (next.dirty) {
|
||||
if (logRenderEvent) {
|
||||
|
||||
Reference in New Issue
Block a user