diff --git a/lib/compose-html-interop/build.gradle.kts b/lib/compose-html-interop/build.gradle.kts
index 514d8635..70bdc7a9 100644
--- a/lib/compose-html-interop/build.gradle.kts
+++ b/lib/compose-html-interop/build.gradle.kts
@@ -1,3 +1,7 @@
+@file:OptIn(ExperimentalWasmDsl::class)
+
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
+
plugins {
id("library-conventions")
id(libs.plugins.kotlin.multiplatform.get().pluginId)
@@ -16,12 +20,14 @@ mavenPublishing {
kotlin {
js(IR) { browser() }
+ wasmJs { browser() }
sourceSets {
- commonMain.dependencies {
- implementation(kotlin("stdlib-js"))
- implementation(compose.foundation)
- }
+ commonMain.dependencies { implementation(compose.foundation) }
+
+ jsMain.dependencies { implementation(kotlin("stdlib-js")) }
+
+ wasmJsMain.dependencies { implementation(libs.kotlinx.browser) }
commonTest.dependencies {
implementation(kotlin("test"))
diff --git a/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlElement.kt b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlElement.kt
index 084758e2..12de7bee 100644
--- a/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlElement.kt
+++ b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlElement.kt
@@ -5,8 +5,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
-import kotlinx.browser.document
-import org.w3c.dom.HTMLElement
@Composable
public fun HtmlElement(
@@ -15,20 +13,9 @@ public fun HtmlElement(
modifier: Modifier = Modifier,
) {
val density = LocalDensity.current
-
- val container =
- rememberDomNode(parent = document.body!!) {
- document.createElement("div").unsafeCast().apply {
- style.position = "absolute"
- style.margin = "0px"
- }
- }
-
+ val container = rememberContainerNode()
val child = rememberDomNode(parent = container, factory = factory)
-
SnapshotEffect(child) { update(it) }
-
Box(modifier.onGloballyPositioned { container.matchLayout(it, density) })
-
HtmlFocusAdapter(container)
}
diff --git a/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlFocusAdapter.kt b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlFocusAdapter.kt
index 0ae1f99a..9a289caa 100644
--- a/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlFocusAdapter.kt
+++ b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlFocusAdapter.kt
@@ -13,7 +13,6 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalFocusManager
-import org.w3c.dom.HTMLElement
@Composable
internal fun HtmlFocusAdapter(container: HTMLElement) {
@@ -29,10 +28,10 @@ internal fun HtmlFocusAdapter(container: HTMLElement) {
modifier =
Modifier.focusRequester(head).onFocusChanged {
if (it.isFocused && !ownFocusRequest) {
- val htmlHead = currentContainer.firstElementChild
+ val htmlHead = currentContainer.headChild
if (htmlHead != null) {
focusManager.clearFocus(force = true)
- htmlHead.unsafeCast().focus()
+ htmlHead.focus()
} else {
ownFocusRequest = true
tail.requestFocus()
@@ -47,10 +46,10 @@ internal fun HtmlFocusAdapter(container: HTMLElement) {
modifier =
Modifier.focusRequester(tail).onFocusChanged {
if (it.isFocused && !ownFocusRequest) {
- val htmlTail = currentContainer.lastElementChild
+ val htmlTail = currentContainer.tailChild
if (htmlTail != null) {
focusManager.clearFocus(force = true)
- htmlTail.unsafeCast().focus()
+ htmlTail.focus()
} else {
ownFocusRequest = true
head.requestFocus()
diff --git a/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/util.kt b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/util.kt
index 5c5bf96c..f8b22e73 100644
--- a/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/util.kt
+++ b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/util.kt
@@ -1,35 +1,23 @@
package dev.sargunv.composehtmlinterop
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.remember
import androidx.compose.ui.layout.LayoutCoordinates
-import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
-import org.w3c.dom.HTMLElement
-import org.w3c.dom.Node
+
+public expect abstract class HTMLElement {
+ public fun focus()
+}
internal fun Dp.toCssValue(): String = "${value}px"
-internal fun HTMLElement.matchLayout(layoutCoordinates: LayoutCoordinates, density: Density) {
- with(density) {
- style.apply {
- val rect = layoutCoordinates.boundsInWindow()
- width = rect.width.toDp().toCssValue()
- height = rect.height.toDp().toCssValue()
- left = rect.left.toDp().toCssValue()
- top = rect.top.toDp().toCssValue()
- }
- }
-}
+@Composable internal expect fun rememberContainerNode(): HTMLElement
+
+internal expect fun HTMLElement.matchLayout(layoutCoordinates: LayoutCoordinates, density: Density)
@Composable
-internal fun rememberDomNode(parent: Node, factory: () -> T): T {
- return remember(key1 = parent, calculation = factory).also { child ->
- DisposableEffect(parent, child) {
- parent.insertBefore(child, parent.firstChild)
- onDispose { parent.removeChild(child) }
- }
- }
-}
+internal expect fun rememberDomNode(parent: HTMLElement, factory: () -> T): T
+
+internal expect val HTMLElement.headChild: HTMLElement?
+
+internal expect val HTMLElement.tailChild: HTMLElement?
diff --git a/lib/compose-html-interop/src/jsMain/kotlin/dev/sargunv/composehtmlinterop/util.js.kt b/lib/compose-html-interop/src/jsMain/kotlin/dev/sargunv/composehtmlinterop/util.js.kt
new file mode 100644
index 00000000..08248944
--- /dev/null
+++ b/lib/compose-html-interop/src/jsMain/kotlin/dev/sargunv/composehtmlinterop/util.js.kt
@@ -0,0 +1,51 @@
+package dev.sargunv.composehtmlinterop
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.boundsInWindow
+import androidx.compose.ui.unit.Density
+import kotlinx.browser.document
+
+public actual typealias HTMLElement = org.w3c.dom.HTMLElement
+
+@Composable
+internal actual fun rememberContainerNode(): HTMLElement =
+ rememberDomNode(parent = document.body!!) {
+ document.createElement("div").unsafeCast().apply {
+ style.position = "absolute"
+ style.margin = "0px"
+ }
+ }
+
+internal actual fun HTMLElement.matchLayout(
+ layoutCoordinates: LayoutCoordinates,
+ density: Density,
+) {
+ with(density) {
+ style.apply {
+ val rect = layoutCoordinates.boundsInWindow()
+ width = rect.width.toDp().toCssValue()
+ height = rect.height.toDp().toCssValue()
+ left = rect.left.toDp().toCssValue()
+ top = rect.top.toDp().toCssValue()
+ }
+ }
+}
+
+@Composable
+internal actual fun rememberDomNode(parent: HTMLElement, factory: () -> T): T {
+ return remember(key1 = parent, calculation = factory).also { child ->
+ DisposableEffect(parent, child) {
+ parent.insertBefore(child, parent.firstChild)
+ onDispose { parent.removeChild(child) }
+ }
+ }
+}
+
+internal actual val HTMLElement.headChild
+ get() = firstElementChild?.unsafeCast()
+
+internal actual val HTMLElement.tailChild
+ get() = lastElementChild?.unsafeCast()
diff --git a/lib/compose-html-interop/src/wasmJsMain/kotlin/dev/sargunv/composehtmlinterop/util.wasmJs.kt b/lib/compose-html-interop/src/wasmJsMain/kotlin/dev/sargunv/composehtmlinterop/util.wasmJs.kt
new file mode 100644
index 00000000..73fbff30
--- /dev/null
+++ b/lib/compose-html-interop/src/wasmJsMain/kotlin/dev/sargunv/composehtmlinterop/util.wasmJs.kt
@@ -0,0 +1,51 @@
+package dev.sargunv.composehtmlinterop
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.boundsInWindow
+import androidx.compose.ui.unit.Density
+import kotlinx.browser.document
+
+public actual typealias HTMLElement = org.w3c.dom.HTMLElement
+
+@Composable
+internal actual fun rememberContainerNode() =
+ rememberDomNode(parent = document.body!!) {
+ document.createElement("div").unsafeCast().apply {
+ style.position = "absolute"
+ style.margin = "0px"
+ }
+ }
+
+internal actual fun HTMLElement.matchLayout(
+ layoutCoordinates: LayoutCoordinates,
+ density: Density,
+) {
+ with(density) {
+ style.apply {
+ val rect = layoutCoordinates.boundsInWindow()
+ width = rect.width.toDp().toCssValue()
+ height = rect.height.toDp().toCssValue()
+ left = rect.left.toDp().toCssValue()
+ top = rect.top.toDp().toCssValue()
+ }
+ }
+}
+
+@Composable
+internal actual fun rememberDomNode(parent: HTMLElement, factory: () -> T): T {
+ return remember(key1 = parent, calculation = factory).also { child ->
+ DisposableEffect(parent, child) {
+ parent.insertBefore(child, parent.firstChild)
+ onDispose { parent.removeChild(child) }
+ }
+ }
+}
+
+internal actual val HTMLElement.headChild
+ get() = firstElementChild?.unsafeCast()
+
+internal actual val HTMLElement.tailChild
+ get() = lastElementChild?.unsafeCast()
diff --git a/lib/kotlin-maplibre-js/MODULE.md b/lib/kotlin-maplibre-js/MODULE.md
index 3e9d3c20..2ab3d9ec 100644
--- a/lib/kotlin-maplibre-js/MODULE.md
+++ b/lib/kotlin-maplibre-js/MODULE.md
@@ -1,3 +1,3 @@
-# Module maplibre-gl-js-kotlin
+# Module kotlin-maplibre-js
Kotlin bindings for [MapLibre GL JS](https://www.npmjs.com/package/maplibre-gl).
diff --git a/lib/kotlin-maplibre-js/build.gradle.kts b/lib/kotlin-maplibre-js/build.gradle.kts
index 31e2dde1..79696799 100644
--- a/lib/kotlin-maplibre-js/build.gradle.kts
+++ b/lib/kotlin-maplibre-js/build.gradle.kts
@@ -7,7 +7,7 @@ plugins {
mavenPublishing {
pom {
name = "MapLibre GL JS Kotlin"
- description = "Kotlin wrapper for MapLibre GL JS."
+ description = "Kotlin bindings for MapLibre GL JS."
url = "https://github.com/sargunv/maplibre-compose"
}
}