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" } }