From d3fef98203a79283c940212a82a05c89558c8c54 Mon Sep 17 00:00:00 2001 From: rnentjes Date: Fri, 19 Sep 2025 19:19:07 +0200 Subject: [PATCH] v.1.2.9 - Fix memory leak --- build.gradle.kts | 2 +- .../nl/astraeus/komp/ElementExtentions.kt | 14 +- .../nl/astraeus/komp/ElementExtentions.kt | 152 ------- .../kotlin/nl/astraeus/komp/ElementIndex.kt | 64 --- .../kotlin/nl/astraeus/komp/HtmlBuilder.kt | 405 ------------------ .../kotlin/nl/astraeus/komp/Komponent.kt | 272 ------------ .../nl/astraeus/komp/KomponentException.kt | 18 - .../komp/MutableCollectionStateDelegate.kt | 54 --- .../kotlin/nl/astraeus/komp/State.kt | 56 --- 9 files changed, 10 insertions(+), 1027 deletions(-) delete mode 100644 src/wasmJsMain/kotlin/nl/astraeus/komp/ElementExtentions.kt delete mode 100644 src/wasmJsMain/kotlin/nl/astraeus/komp/ElementIndex.kt delete mode 100644 src/wasmJsMain/kotlin/nl/astraeus/komp/HtmlBuilder.kt delete mode 100644 src/wasmJsMain/kotlin/nl/astraeus/komp/Komponent.kt delete mode 100644 src/wasmJsMain/kotlin/nl/astraeus/komp/KomponentException.kt delete mode 100644 src/wasmJsMain/kotlin/nl/astraeus/komp/MutableCollectionStateDelegate.kt delete mode 100644 src/wasmJsMain/kotlin/nl/astraeus/komp/State.kt diff --git a/build.gradle.kts b/build.gradle.kts index 5acdc44..9c7ba05 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,7 +11,7 @@ plugins { } group = "nl.astraeus" -version = "1.2.8" +version = "1.2.9" repositories { mavenCentral() diff --git a/src/jsMain/kotlin/nl/astraeus/komp/ElementExtentions.kt b/src/jsMain/kotlin/nl/astraeus/komp/ElementExtentions.kt index 79dd569..87a36b4 100644 --- a/src/jsMain/kotlin/nl/astraeus/komp/ElementExtentions.kt +++ b/src/jsMain/kotlin/nl/astraeus/komp/ElementExtentions.kt @@ -1,9 +1,8 @@ package nl.astraeus.komp -import org.w3c.dom.events.Event import org.w3c.dom.Element import org.w3c.dom.HTMLInputElement -import org.w3c.dom.events.EventListener +import org.w3c.dom.events.Event import org.w3c.dom.get private fun Int.asSpaces(): String { @@ -129,10 +128,15 @@ internal fun Element.setKompEvent(name: String, event: (Event) -> Unit) { this.addEventListener(eventName, event) } -private val kompEvents = mutableMapOf Unit>>() - internal fun Element.getKompEvents(): MutableMap Unit> { - return kompEvents.getOrPut(this) { mutableMapOf() } + var map = this.asDynamic()["komp-events"] as? MutableMap Unit> + + if (map == null) { + map = mutableMapOf() + this.asDynamic()["komp-events"] = map + } + + return map } internal fun Element.findElementIndex(): Int { diff --git a/src/wasmJsMain/kotlin/nl/astraeus/komp/ElementExtentions.kt b/src/wasmJsMain/kotlin/nl/astraeus/komp/ElementExtentions.kt deleted file mode 100644 index c81f813..0000000 --- a/src/wasmJsMain/kotlin/nl/astraeus/komp/ElementExtentions.kt +++ /dev/null @@ -1,152 +0,0 @@ -package nl.astraeus.komp - -import org.w3c.dom.Element -import org.w3c.dom.HTMLInputElement -import org.w3c.dom.events.Event -import org.w3c.dom.get - -private fun Int.asSpaces(): String { - val result = StringBuilder() - - repeat(this) { - result.append(" ") - } - return result.toString() -} - -fun Element.printTree(indent: Int = 0): String { - val result = StringBuilder() - - result.append(indent.asSpaces()) - result.append(tagName) - if (this.namespaceURI != "http://www.w3.org/1999/xhtml") { - result.append(" [") - result.append(namespaceURI) - result.append("]") - } - result.append(" (") - var first = true - if (hasAttributes()) { - for (index in 0 until attributes.length) { - if (!first) { - result.append(", ") - } else { - first = false - } - result.append(attributes[index]?.localName) - result.append("=") - result.append(attributes[index]?.value) - } - } - result.append(") {") - 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) { - childNodes[index]?.let { - if (it is Element) { - result.append(it.printTree(indent + 2)) - } else { - result.append((indent + 2).asSpaces()) - result.append(it.textContent) - result.append("\n") - } - } - } - result.append(indent.asSpaces()) - result.append("}\n") - - return result.toString() -} - -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 if (attributeName == "class") { - className = "" - } else { - removeAttribute(attributeName) - } - } else { - if (this is HTMLInputElement) { - when (attributeName) { - "checked" -> { - checked = "checked" == value - } - "class" -> { - className = value - } - "value" -> { - this.value = value - } - else -> { - setAttribute(attributeName, value) - } - } - } else if (attributeName == "class") { - className = value - } else if (this.getAttribute(attributeName) != value) { - setAttribute(attributeName, value) - } - } -} - -internal fun Element.clearKompEvents() { - val events = getKompEvents() - for ((name, event) in getKompEvents()) { - removeEventListener(name, event) - } - events.clear() -} - -internal fun Element.setKompEvent(name: String, event: (Event) -> Unit) { - val eventName: String = if (name.startsWith("on")) { - name.substring(2) - } else { - name - } - - getKompEvents()[eventName] = event - - this.addEventListener(eventName, event) // AddEventListenerOptions(true)) -} - -private val kompEvents = mutableMapOf Unit>>() - -internal fun Element.getKompEvents(): MutableMap Unit> { - return kompEvents.getOrPut(this) { mutableMapOf() } -} - -internal fun Element.findElementIndex(): Int { - val childNodes = parentElement?.children - if (childNodes != null) { - for (index in 0 until childNodes.length) { - if (childNodes[index] == this) { - return index - } - } - } - - return 0 -} diff --git a/src/wasmJsMain/kotlin/nl/astraeus/komp/ElementIndex.kt b/src/wasmJsMain/kotlin/nl/astraeus/komp/ElementIndex.kt deleted file mode 100644 index ea34fc8..0000000 --- a/src/wasmJsMain/kotlin/nl/astraeus/komp/ElementIndex.kt +++ /dev/null @@ -1,64 +0,0 @@ -package nl.astraeus.komp - -import org.w3c.dom.Node -import org.w3c.dom.get - -data class ElementIndex( - val parent: Node, - var childIndex: Int, - var setAttr: MutableSet = mutableSetOf() -) { - override fun toString(): String { - return "${parent.nodeName}[$childIndex]" - } -} - -fun ArrayList.currentParent(): Node { - this.lastOrNull()?.let { - return it.parent - } - - throw IllegalStateException("currentParent should never be null!") -} - -fun ArrayList.currentElement(): Node? { - this.lastOrNull()?.let { - return it.parent.childNodes[it.childIndex] - } - - return null -} - -fun ArrayList.currentPosition(): ElementIndex? { - return if (this.size < 2) { - null - } else { - this[this.size - 2] - } -} - -fun ArrayList.nextElement() { - this.lastOrNull()?.let { - it.setAttr.clear() - it.childIndex++ - } -} - -fun ArrayList.pop() { - this.removeLast() -} - -fun ArrayList.push(element: Node) { - this.add(ElementIndex(element, 0)) -} - -fun ArrayList.replace(new: Node) { - if (this.currentElement() != null) { - this.currentElement()?.parentElement?.replaceChild( - new, - this.currentElement()!! - ) - } else { - this.last().parent.appendChild(new) - } -} diff --git a/src/wasmJsMain/kotlin/nl/astraeus/komp/HtmlBuilder.kt b/src/wasmJsMain/kotlin/nl/astraeus/komp/HtmlBuilder.kt deleted file mode 100644 index e8d521c..0000000 --- a/src/wasmJsMain/kotlin/nl/astraeus/komp/HtmlBuilder.kt +++ /dev/null @@ -1,405 +0,0 @@ -package nl.astraeus.komp - -import kotlinx.browser.document -import kotlinx.html.DefaultUnsafe -import kotlinx.html.Entities -import kotlinx.html.FlowOrMetaDataOrPhrasingContent -import kotlinx.html.Tag -import kotlinx.html.TagConsumer -import kotlinx.html.Unsafe -import org.w3c.dom.Element -import org.w3c.dom.HTMLElement -import org.w3c.dom.HTMLInputElement -import org.w3c.dom.HTMLSpanElement -import org.w3c.dom.Node -import org.w3c.dom.asList -import org.w3c.dom.get - -private var currentElement: Element? = null - -interface HtmlConsumer : TagConsumer { - fun append(node: Element) - fun include(komponent: Komponent) - fun debug(block: HtmlConsumer.() -> Unit) -} - -fun FlowOrMetaDataOrPhrasingContent.currentElement(): Element = - currentElement ?: error("No current element defined!") - -private fun Node.asElement() = this as? HTMLElement - -class HtmlBuilder( - private val komponent: Komponent?, - parent: Element, - childIndex: Int = 0 -) : HtmlConsumer { - private var currentPosition = arrayListOf() - private var inDebug = false - private var exceptionThrown = false - private var currentNode: Node? = null - private var firstTag: Boolean = true - var root: Element? = null - - init { - currentPosition.add(ElementIndex(parent, childIndex)) - } - - override fun include(komponent: Komponent) { - if ( - komponent.element != null && - !komponent.memoizeChanged() - ) { - currentPosition.replace(komponent.element!!) - if (Komponent.logRenderEvent) { - println( - "Skipped include $komponent, memoize hasn't changed" - ) - } - } else { - // current element should become parent -/* - val ce = komponent.element - if (ce != null) { - append(ce as Element) - } -*/ - komponent.create( - currentPosition.last().parent as Element, - currentPosition.last().childIndex - ) - } - currentPosition.nextElement() - } - - override fun append(node: Element) { - currentPosition.replace(node) - currentPosition.nextElement() - } - - override fun debug(block: HtmlConsumer.() -> Unit) { - val enableAssertions = Komponent.enableAssertions - Komponent.enableAssertions = true - inDebug = true - - try { - block.invoke(this) - } finally { - inDebug = false - Komponent.enableAssertions = enableAssertions - } - } - - private fun logReplace(msg: () -> String) { - if (Komponent.logReplaceEvent && inDebug) { - println(msg.invoke()) - } - } - - override fun onTagStart(tag: Tag) { - logReplace { - "onTagStart, [${tag.tagName}, ${tag.namespace ?: ""}], currentPosition: $currentPosition" - } - - currentNode = currentPosition.currentElement() - - if (currentNode == null) { - logReplace { "onTagStart, currentNode1: $currentNode" } - currentNode = if (tag.namespace != null) { - document.createElementNS(tag.namespace, tag.tagName) - } else { - document.createElement(tag.tagName) - } - - logReplace { "onTagStart, currentElement1.1: $currentNode" } - currentPosition.currentParent().appendChild(currentNode!!) - } else if ( - !currentNode?.asElement()?.tagName.equals(tag.tagName, true) || - ( - tag.namespace != null && - !currentNode?.asElement()?.namespaceURI.equals(tag.namespace, true) - ) - ) { - logReplace { - "onTagStart, currentElement, namespace: ${currentNode?.asElement()?.namespaceURI} -> ${tag.namespace}" - } - logReplace { - "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!!) - } - - currentElement = currentNode as? Element ?: currentElement - - if (currentNode is Element) { - if (firstTag) { - logReplace { "Setting root: $currentNode" } - root = currentNode as Element - 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) { - currentElement!!.setKompAttribute(entry.key, entry.value) - currentPosition.lastOrNull()?.setAttr?.add(entry.key) - } - } - - currentPosition.push(currentNode!!) - } - - private fun checkTag(source: String, tag: Tag) { - check(currentElement != null) { - "No current tag ($source)" - } - check(currentElement?.tagName.equals(tag.tagName, ignoreCase = true)) { - "Wrong current tag ($source), got: ${tag.tagName} expected ${currentElement?.tagName}" - } - } - - override fun onTagAttributeChange( - tag: Tag, - attribute: String, - value: String? - ) { - logReplace { "onTagAttributeChange, ${tag.tagName} [$attribute, $value]" } - - if (Komponent.enableAssertions) { - checkTag("onTagAttributeChange", tag) - } - - currentElement?.setKompAttribute(attribute, value) - if (value == null || value.isEmpty()) { - currentPosition.currentPosition()?.setAttr?.remove(attribute) - } else { - currentPosition.currentPosition()?.setAttr?.add(attribute) - } - } - - override fun onTagEvent( - tag: Tag, - event: String, - value: (kotlinx.html.org.w3c.dom.events.Event) -> Unit - ) { - logReplace { "onTagEvent, ${tag.tagName} [$event, $value]" } - - if (Komponent.enableAssertions) { - checkTag("onTagEvent", tag) - } - - currentElement?.setKompEvent(event.lowercase(), value) - } - - override fun onTagEnd(tag: Tag) { - logReplace { - "onTagEnd, [${tag.tagName}, ${tag.namespace}], currentPosition: $currentPosition" - } - - if (exceptionThrown) { - return - } - - while (currentPosition.currentElement() != null) { - currentPosition.currentElement()?.let { - it.parentElement?.removeChild(it) - } - } - - if (Komponent.enableAssertions) { - checkTag("onTagEnd", tag) - } - - if (currentElement != null) { - val setAttrs: Set = 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) && - attr != "style" - ) { - element.setKompAttribute(attr, null) - } - } - } - } - } - - currentPosition.pop() - - currentNode = currentPosition.currentElement() - currentElement = currentNode as? Element ?: currentElement - - currentPosition.nextElement() - - currentElement = currentElement?.parentElement as? HTMLElement - - //logReplace"onTagEnd, popped: $currentElement") - } - - override fun onTagContent(content: CharSequence) { - //logReplace"onTagContent, [$content]") - - check(currentElement != null) { - "No current DOM node" - } - - //logReplace"Tag content: $content") - if ( - currentElement?.nodeType != Node.TEXT_NODE || - currentElement?.textContent != content.toString() - ) { - currentElement?.textContent = content.toString() - } - - currentPosition.nextElement() - } - - override fun onTagContentEntity(entity: Entities) { - //logReplace"onTagContentEntity, [${entity.text}]") - - check(currentElement != null) { - "No current DOM node" - } - - val s = document.createElement("span") as HTMLSpanElement - s.innerHTML = entity.text - currentPosition.replace( - s.childNodes.asList().firstOrNull() ?: document.createTextNode(entity.text) - ) - currentPosition.nextElement() - } - - override fun onTagContentUnsafe(block: Unsafe.() -> Unit) { - with(DefaultUnsafe()) { - block() - - val textContent = toString() - - //logReplace"onTagContentUnsafe, [$textContent]") - - var namespace: String? = null - - if (currentPosition.currentParent().nodeType == 1.toShort()) { - val element = currentPosition.currentParent() as Element - - namespace = when (Komponent.unsafeMode) { - UnsafeMode.UNSAFE_ALLOWED -> { - element.namespaceURI - } - UnsafeMode.UNSAFE_SVG_ONLY -> { - if (element.namespaceURI == "http://www.w3.org/2000/svg") { - element.namespaceURI - } else { - null - } - } - else -> { - null - } - } - } - - //logReplace"onTagContentUnsafe, namespace: [$namespace]") - - if (Komponent.unsafeMode == UnsafeMode.UNSAFE_ALLOWED || - ( - Komponent.unsafeMode == UnsafeMode.UNSAFE_SVG_ONLY && - namespace == "http://www.w3.org/2000/svg" - ) - ) { - if (currentElement?.innerHTML != textContent) { - currentElement?.innerHTML += textContent - } - } else if (currentElement?.textContent != textContent) { - currentElement?.textContent = textContent - } - - currentPosition.nextElement() - } - } - - override fun onTagComment(content: CharSequence) { - //logReplace"onTagComment, [$content]") - - check(currentElement != null) { - "No current DOM node" - } - currentElement?.appendChild( - document.createComment(content.toString()) - ) - - currentPosition.nextElement() - } - - fun onTagError(tag: Tag, exception: Throwable) { - exceptionThrown = true - - if (exception !is KomponentException) { - val position = mutableListOf() - var ce = currentElement - while (ce != null) { - position.add(ce) - ce = ce.parentElement - } - val builder = StringBuilder() - for (element in position.reversed()) { - builder.append("> ") - builder.append(element.tagName) - builder.append("[") - builder.append(element.findElementIndex()) - builder.append("]") - if (element.hasAttribute("class")) { - builder.append("(") - builder.append(element.getAttribute("class")) - builder.append(")") - } - builder.append(" ") - } - - throw KomponentException( - komponent, - currentElement, - tag, - builder.toString(), - exception.message ?: "error", - exception - ) - } else { - throw exception - } - } - - override fun finalize(): Element { - //logReplace"finalize, currentPosition: $currentPosition") - return root ?: throw IllegalStateException( - "We can't finalize as there was no tags" - ) - } - - companion object { - fun create(content: HtmlBuilder.() -> Unit): Element { - val container = document.createElement("div") as HTMLElement - val consumer = HtmlBuilder(null, container) - content.invoke(consumer) - return consumer.root ?: error("No root element found after render!") - } - } -} diff --git a/src/wasmJsMain/kotlin/nl/astraeus/komp/Komponent.kt b/src/wasmJsMain/kotlin/nl/astraeus/komp/Komponent.kt deleted file mode 100644 index e907060..0000000 --- a/src/wasmJsMain/kotlin/nl/astraeus/komp/Komponent.kt +++ /dev/null @@ -1,272 +0,0 @@ -package nl.astraeus.komp - -import kotlinx.browser.window -import kotlinx.html.FlowOrMetaDataOrPhrasingContent -import org.w3c.dom.Element -import org.w3c.dom.HTMLElement -import org.w3c.dom.get - -private var currentKomponent: Komponent? = null - -fun FlowOrMetaDataOrPhrasingContent.currentKomponent(): Komponent = - currentKomponent ?: error("No current komponent defined! Only call from render code!") - -enum class UnsafeMode { - UNSAFE_ALLOWED, - UNSAFE_DISABLED, - UNSAFE_SVG_ONLY -} - -var Element.memoizeHash: String? - get() { - return getAttribute("memoize-hash") - } - set(value) { - if (value != null) { - setAttribute("memoize-hash", value.toString()) - } else { - removeAttribute("memoize-hash") - } - } - -abstract class Komponent { - val createIndex = getNextCreateIndex() - private var dirty: Boolean = true - - var element: Element? = null - - open fun create(parent: Element, childIndex: Int? = null) { - onBeforeUpdate() - val builder = HtmlBuilder( - this, - parent, - childIndex ?: parent.childNodes.length - ) - - try { - currentKomponent = this - builder.render() - } catch(e: KomponentException) { - errorHandler(e) - } finally { - currentKomponent = null - } - - element = builder.root - updateMemoizeHash() - onAfterUpdate() - } - - fun memoizeChanged() = element?.memoizeHash == null || element?.memoizeHash != fullMemoizeHash() - - fun updateMemoizeHash() { - element?.memoizeHash = fullMemoizeHash() - } - - private fun fullMemoizeHash(): String? { - val generated = generateMemoizeHash() - - return if (generated != null) { - "${this::class.simpleName}:${generateMemoizeHash()}" - } else { - null - } - } - - abstract fun HtmlBuilder.render() - - /** - * This method is called after the Komponent is updated - * - * note: it's also called at first render - */ - open fun onAfterUpdate() {} - - /** - * This method is called before the Komponent is updated - * and before memoizeHash is checked - * - * note: it's also called at first render - */ - open fun onBeforeUpdate() {} - - fun requestUpdate() { - dirty = true - scheduleForUpdate(this) - } - - /** - * Request an immediate update of this Komponent - * - * This will run immediately, make sure Komponents are not rendered multiple times - * Any scheduled updates will be run as well - */ - fun requestImmediateUpdate() { - dirty = true - runUpdateImmediately(this) - } - - /** - * This function can be overwritten if you know how to update the Komponent yourself - * - * HTMLBuilder.render() is called 1st time the component is rendered, after that this - * method will be called - * - * @deprecated - */ - @Deprecated( - "Deprecated to avoid confusing with requestUpdate, use renderUpdate instead", - ReplaceWith("renderUpdate"), - level = DeprecationLevel.WARNING - ) - protected fun update() { - refresh() - } - - /** - * This function can be overwritten if you know how to update the Komponent yourself - * - * HTMLBuilder.render() is called 1st time the component is rendered, after that this - * method will be called - */ - open fun renderUpdate() { - refresh() - } - - /** - * If this function returns a value it will be stored and on the next render it will be compared. - * - * The render will only happen if the hash is not null and has changed - */ - open fun generateMemoizeHash(): Int? = null - - private fun refresh() { - val currentElement = element - - check(currentElement != null) { - error("element is null") - } - - val parent = currentElement.parentElement as? HTMLElement ?: error("parent is null!?") - var childIndex = 0 - for (index in 0 until parent.childNodes.length) { - if (parent.childNodes[index] == currentElement) { - childIndex = index - } - } - val builder = HtmlBuilder(this, parent, childIndex) - - try { - currentKomponent = this - builder.render() - } catch(e: KomponentException) { - errorHandler(e) - } finally { - currentKomponent = null - } - - element = builder.root - dirty = false - } - - override fun toString(): String { - return "${this::class.simpleName}" - } - - companion object { - private var nextCreateIndex: Int = 1 - private var updateCallback: Int? = null - private var errorHandler: (KomponentException) -> Unit = { ke -> - println("Render error in Komponent: $ke") - - ke.element?.innerHTML = """
Render error!
""" - - window.alert(""" - Error in Komponent '${ke.komponent}', ${ke.message} - Tag: ${ke.tag.tagName} - See console log for details - Position: ${ke.position}""".trimIndent() - ) - } - private var scheduledForUpdate = mutableSetOf() - private var interceptor: (Komponent, () -> Unit) -> Unit = { _, block -> block() } - - var logRenderEvent = false - var logReplaceEvent = false - var enableAssertions = false - var unsafeMode = UnsafeMode.UNSAFE_DISABLED - - fun create(parent: HTMLElement, component: Komponent) { - component.create(parent) - } - - fun setErrorHandler(handler: (KomponentException) -> Unit) { - errorHandler = handler - } - - fun setUpdateInterceptor(block: (Komponent, () -> Unit) -> Unit) { - interceptor = block - } - - private fun getNextCreateIndex() = nextCreateIndex++ - - private fun scheduleForUpdate(komponent: Komponent) { - scheduledForUpdate.add(komponent) - - if (updateCallback == null) { - updateCallback = window.setTimeout({ - runUpdate() - }, 0) - } - } - - private fun runUpdateImmediately(komponent: Komponent) { - scheduledForUpdate.add(komponent) - runUpdate() - } - - private fun runUpdate(): JsAny { - val todo = scheduledForUpdate.sortedBy { komponent -> komponent.createIndex } - - if (logRenderEvent) { - println("runUpdate") - } - - todo.forEach { next -> - interceptor(next) { - val element = next.element - - if (element is HTMLElement) { - if (next.dirty) { - if (logRenderEvent) { - println("Update dirty ${next.createIndex}") - } - val memoizeHash = next.generateMemoizeHash() - - if (next.memoizeChanged()) { - next.onBeforeUpdate() - next.renderUpdate() - next.updateMemoizeHash() - next.onAfterUpdate() - } else if (logRenderEvent) { - println("Skipped render, memoizeHash is equal $next-[$memoizeHash]") - } - } else { - if (logRenderEvent) { - println("Skip ${next.createIndex}") - } - } - } else { - println("Komponent element is null $next, $element") - } - } - } - - scheduledForUpdate.clear() - updateCallback = null - - return "JsAny".toJsString() - } - } - -} diff --git a/src/wasmJsMain/kotlin/nl/astraeus/komp/KomponentException.kt b/src/wasmJsMain/kotlin/nl/astraeus/komp/KomponentException.kt deleted file mode 100644 index a2bc763..0000000 --- a/src/wasmJsMain/kotlin/nl/astraeus/komp/KomponentException.kt +++ /dev/null @@ -1,18 +0,0 @@ -package nl.astraeus.komp - -import kotlinx.html.Tag -import org.w3c.dom.Element - -class KomponentException( - val komponent: Komponent?, - val element: Element?, - val tag: Tag, - val position: String, - message: String, - cause: Throwable -) : RuntimeException(message, cause) { - - override fun toString(): String { - return "KompException(message='$message', tag='$tag', position='$position')" - } -} diff --git a/src/wasmJsMain/kotlin/nl/astraeus/komp/MutableCollectionStateDelegate.kt b/src/wasmJsMain/kotlin/nl/astraeus/komp/MutableCollectionStateDelegate.kt deleted file mode 100644 index 76c63e3..0000000 --- a/src/wasmJsMain/kotlin/nl/astraeus/komp/MutableCollectionStateDelegate.kt +++ /dev/null @@ -1,54 +0,0 @@ -package nl.astraeus.komp - -inline fun Komponent.mutableCollectionState( - initialValue: MutableCollection -): MutableCollection = MutableCollectionStateDelegate( - this, - initialValue -) - -class MutableCollectionStateDelegate( - val komponent: Komponent, - val collection: MutableCollection -): MutableCollection by collection { - - override fun add(element: T): Boolean { - komponent.requestUpdate() - - return collection.add(element) - } - - override fun addAll(elements: Collection): Boolean { - komponent.requestUpdate() - - return collection.addAll(elements) - } - - override fun clear() { - komponent.requestUpdate() - - collection.clear() - } - - // todo: return iterator wrapper to update at changes? - //override fun iterator(): MutableIterator = collection.iterator() - - override fun remove(element: T): Boolean { - komponent.requestUpdate() - - return collection.remove(element) - } - - override fun removeAll(elements: Collection): Boolean { - komponent.requestUpdate() - - return collection.removeAll(elements) - } - - override fun retainAll(elements: Collection): Boolean { - komponent.requestUpdate() - - return collection.retainAll(elements) - } - -} diff --git a/src/wasmJsMain/kotlin/nl/astraeus/komp/State.kt b/src/wasmJsMain/kotlin/nl/astraeus/komp/State.kt deleted file mode 100644 index a479204..0000000 --- a/src/wasmJsMain/kotlin/nl/astraeus/komp/State.kt +++ /dev/null @@ -1,56 +0,0 @@ -package nl.astraeus.komp - -import kotlin.reflect.KProperty - -interface Delegate { - - operator fun getValue( - thisRef: Any?, - property: KProperty<*> - ): T - - operator fun setValue( - thisRef: Any?, - property: KProperty<*>, - value: T - ) - -} - -open class StateDelegate( - val komponent: Komponent, - initialValue: T -) : Delegate { - private var value: T = initialValue - - init { - if (value is MutableCollection<*>) { - error("Use mutableList to create a collection!") - } - } - - override operator fun getValue( - thisRef: Any?, - property: KProperty<*> - ): T { - return value - } - - override operator fun setValue( - thisRef: Any?, - property: KProperty<*>, - value: T - ) { - if (this.value?.equals(value) != true) { - this.value = value - komponent.requestUpdate() - } - } -} - -inline fun Komponent.state( - initialValue: T -): Delegate = StateDelegate( - this, - initialValue -)