diff --git a/.idea/runConfigurations/browser.xml b/.idea/runConfigurations/browserJs.xml
similarity index 85%
rename from .idea/runConfigurations/browser.xml
rename to .idea/runConfigurations/browserJs.xml
index 159625a..905d0c0 100644
--- a/.idea/runConfigurations/browser.xml
+++ b/.idea/runConfigurations/browserJs.xml
@@ -1,5 +1,5 @@
-
+
@@ -18,7 +18,7 @@
true
true
false
- false
+ false
\ No newline at end of file
diff --git a/.idea/runConfigurations/browserWasmJs.xml b/.idea/runConfigurations/browserWasmJs.xml
new file mode 100644
index 0000000..2cdacd1
--- /dev/null
+++ b/.idea/runConfigurations/browserWasmJs.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 7267b47..7bc736c 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,6 +1,7 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat.Deb
import org.jetbrains.compose.desktop.application.dsl.TargetFormat.Dmg
import org.jetbrains.compose.desktop.application.dsl.TargetFormat.Msi
+import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
plugins {
kotlin("multiplatform")
@@ -58,6 +59,12 @@ kotlin {
binaries.executable()
}
+ @OptIn(ExperimentalWasmDsl::class)
+ wasmJs {
+ browser()
+ binaries.executable()
+ }
+
sourceSets {
val commonMain by getting {
dependencies {
diff --git a/app/src/wasmJsMain/kotlin/io/github/xxfast/decompose/router/app/Application.kt b/app/src/wasmJsMain/kotlin/io/github/xxfast/decompose/router/app/Application.kt
new file mode 100644
index 0000000..6247308
--- /dev/null
+++ b/app/src/wasmJsMain/kotlin/io/github/xxfast/decompose/router/app/Application.kt
@@ -0,0 +1,25 @@
+package io.github.xxfast.decompose.router.app
+
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.window.CanvasBasedWindow
+import io.github.xxfast.decompose.router.LocalRouterContext
+import io.github.xxfast.decompose.router.RouterContext
+import io.github.xxfast.decompose.router.screens.HomeScreen
+import io.github.xxfast.decompose.router.defaultRouterContext
+
+@OptIn(ExperimentalComposeUiApi::class)
+fun main() {
+ val rootRouterContext: RouterContext = defaultRouterContext()
+
+ CanvasBasedWindow(canvasElementId = "ComposeTarget") {
+ CompositionLocalProvider(
+ LocalRouterContext provides rootRouterContext,
+ ) {
+ MaterialTheme {
+ HomeScreen()
+ }
+ }
+ }
+}
diff --git a/app/src/wasmJsMain/resources/index.html b/app/src/wasmJsMain/resources/index.html
new file mode 100644
index 0000000..058e262
--- /dev/null
+++ b/app/src/wasmJsMain/resources/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+ App
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/wasmJsMain/resources/styles.css b/app/src/wasmJsMain/resources/styles.css
new file mode 100644
index 0000000..46c48b1
--- /dev/null
+++ b/app/src/wasmJsMain/resources/styles.css
@@ -0,0 +1,14 @@
+html, body {
+ height: 100%;
+ margin: 0px;
+ padding: 0px;
+}
+
+canvas {
+ width: 100vw;
+ height: 100vh;
+ display: block;
+ position: fixed;
+ top: 0;
+ left: 0;
+}
diff --git a/decompose-router/build.gradle.kts b/decompose-router/build.gradle.kts
index 93e37b5..89e8f75 100644
--- a/decompose-router/build.gradle.kts
+++ b/decompose-router/build.gradle.kts
@@ -1,4 +1,5 @@
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
+import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
plugins {
kotlin("multiplatform")
@@ -28,6 +29,11 @@ kotlin {
browser()
}
+ @OptIn(ExperimentalWasmDsl::class)
+ wasmJs {
+ browser()
+ }
+
sourceSets {
val commonMain by getting {
dependencies {
@@ -58,8 +64,6 @@ kotlin {
}
}
- val jsMain by getting
-
val androidMain by getting {
dependencies {
implementation(compose.material3)
@@ -83,6 +87,12 @@ kotlin {
implementation(libs.compose.ui.test.manifest)
}
}
+
+ val jsMain by getting {
+ dependencies {
+ implementation("org.jetbrains.kotlin-wrappers:kotlin-browser:1.0.0-pre.752")
+ }
+ }
}
}
diff --git a/decompose-router/src/jsMain/kotlin/io/github/xxfast/decompose/router/DefaultRouterContext.kt b/decompose-router/src/jsMain/kotlin/io/github/xxfast/decompose/router/DefaultRouterContext.kt
index 91f39af..d6ba47c 100644
--- a/decompose-router/src/jsMain/kotlin/io/github/xxfast/decompose/router/DefaultRouterContext.kt
+++ b/decompose-router/src/jsMain/kotlin/io/github/xxfast/decompose/router/DefaultRouterContext.kt
@@ -5,7 +5,8 @@ import com.arkivanov.essenty.lifecycle.LifecycleRegistry
import com.arkivanov.essenty.lifecycle.resume
import com.arkivanov.essenty.lifecycle.stop
import kotlinx.browser.document
-import org.w3c.dom.Document
+import web.dom.DocumentVisibilityState
+import web.dom.document as webDocument
fun defaultRouterContext(): RouterContext {
val backDispatcher = BackDispatcher()
@@ -16,11 +17,12 @@ fun defaultRouterContext(): RouterContext {
// Attaches the LifecycleRegistry to the document
private fun LifecycleRegistry.attachToDocument() {
- fun onVisibilityChanged() = if (document.visibilityState == "visible") resume() else stop()
+ fun onVisibilityChanged() =
+ if (webDocument.visibilityState == DocumentVisibilityState.visible) resume()
+ else stop()
+
onVisibilityChanged()
- document.addEventListener(type = "visibilitychange", callback = { onVisibilityChanged() })
-}
-private val Document.visibilityState: String
- get() = asDynamic().visibilityState.unsafeCast()
+ document.addEventListener("visibilitychange", callback = { onVisibilityChanged() })
+}
diff --git a/decompose-router/src/wasmJsMain/kotlin/io/github/xxfast/decompose/router/DefaultRouterContext.kt b/decompose-router/src/wasmJsMain/kotlin/io/github/xxfast/decompose/router/DefaultRouterContext.kt
new file mode 100644
index 0000000..17f5a2b
--- /dev/null
+++ b/decompose-router/src/wasmJsMain/kotlin/io/github/xxfast/decompose/router/DefaultRouterContext.kt
@@ -0,0 +1,27 @@
+package io.github.xxfast.decompose.router
+
+import com.arkivanov.essenty.backhandler.BackDispatcher
+import com.arkivanov.essenty.lifecycle.LifecycleRegistry
+import com.arkivanov.essenty.lifecycle.resume
+import com.arkivanov.essenty.lifecycle.stop
+import kotlinx.browser.document
+import org.w3c.dom.Document
+
+fun defaultRouterContext(): RouterContext {
+ val backDispatcher = BackDispatcher()
+ val lifecycle = LifecycleRegistry()
+ lifecycle.attachToDocument()
+ return RouterContext(lifecycle = lifecycle, backHandler = backDispatcher)
+}
+
+// Attaches the LifecycleRegistry to the document
+private fun LifecycleRegistry.attachToDocument() {
+ fun onVisibilityChanged() = if (visibilityState(document) == "visible") resume() else stop()
+ onVisibilityChanged()
+ document.addEventListener(type = "visibilitychange", callback = { onVisibilityChanged() })
+}
+
+// Workaround for Document#visibilityState not available in Wasm
+// From https://github.com/arkivanov/Minesweeper/blob/8270ffb0c75bf032b6d4da673c0bb2b01c9496ec/composeApp/src/wasmJsMain/kotlin/Main.kt#L47
+@JsFun("(document) => document.visibilityState")
+private external fun visibilityState(document: Document): String
diff --git a/decompose-router/src/wasmJsMain/kotlin/io/github/xxfast/decompose/router/Key.kt b/decompose-router/src/wasmJsMain/kotlin/io/github/xxfast/decompose/router/Key.kt
new file mode 100644
index 0000000..2ec0c5a
--- /dev/null
+++ b/decompose-router/src/wasmJsMain/kotlin/io/github/xxfast/decompose/router/Key.kt
@@ -0,0 +1,7 @@
+package io.github.xxfast.decompose.router
+
+import kotlin.reflect.KClass
+
+// TODO: Given that we don't have tree-shaking on js - yet, should be safe to use simpleName here
+actual val KClass<*>.key: String get() =
+ requireNotNull(simpleName) { "Unable to use name of $this as the default key"}
diff --git a/gradle.properties b/gradle.properties
index 4eac971..9224226 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -8,9 +8,11 @@ android.nonTransitiveRClass=true
#MPP
kotlin.mpp.enableCInteropCommonization=true
kotlin.android.buildTypeAttribute.keep=true
+
# Compose-multiplatform
org.jetbrains.compose.experimental.uikit.enabled=true
org.jetbrains.compose.experimental.jscanvas.enabled=true
+org.jetbrains.compose.experimental.wasm.enabled=true
# Optin to new Android source set layout
# https://kotlinlang.org/docs/whatsnew18.html#kotlin-multiplatform-a-new-android-source-set-layout