Skip to content

Commit 22b6f7e

Browse files
committed
Add web app
1 parent b565473 commit 22b6f7e

6 files changed

Lines changed: 2943 additions & 0 deletions

File tree

app/build.gradle.kts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ kotlin {
3939
}
4040
}
4141

42+
js(IR) {
43+
browser()
44+
binaries.executable()
45+
}
46+
4247
sourceSets {
4348
val commonMain by getting {
4449
dependencies {
@@ -119,3 +124,8 @@ compose.desktop {
119124
}
120125
}
121126
}
127+
128+
compose.experimental {
129+
web.application {
130+
}
131+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.github.xxfast.decompose.router.app
2+
3+
import androidx.compose.material3.MaterialTheme
4+
import androidx.compose.runtime.CompositionLocalProvider
5+
import com.arkivanov.essenty.lifecycle.LifecycleRegistry
6+
import io.github.xxfast.decompose.router.LocalRouterContext
7+
import io.github.xxfast.decompose.router.RouterContext
8+
import io.github.xxfast.decompose.router.app.screens.HomeScreen
9+
import io.github.xxfast.decompose.router.app.utils.BrowserViewportWindow
10+
import org.jetbrains.skiko.wasm.onWasmReady
11+
12+
fun main() {
13+
onWasmReady {
14+
val lifecycle = LifecycleRegistry()
15+
val rootRouterContext = RouterContext(lifecycle = lifecycle)
16+
17+
BrowserViewportWindow("App") {
18+
CompositionLocalProvider(
19+
LocalRouterContext provides rootRouterContext,
20+
) {
21+
MaterialTheme {
22+
HomeScreen()
23+
}
24+
}
25+
}
26+
}
27+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
@file:Suppress(
2+
"INVISIBLE_MEMBER",
3+
"INVISIBLE_REFERENCE",
4+
"EXPOSED_PARAMETER_TYPE"
5+
)
6+
7+
package io.github.xxfast.decompose.router.app.utils // WORKAROUND: ComposeWindow and ComposeLayer are internal
8+
9+
import androidx.compose.runtime.Composable
10+
import androidx.compose.ui.window.ComposeWindow
11+
import kotlinx.browser.document
12+
import kotlinx.browser.window
13+
import org.w3c.dom.HTMLCanvasElement
14+
import org.w3c.dom.HTMLStyleElement
15+
import org.w3c.dom.HTMLTitleElement
16+
17+
private const val CANVAS_ELEMENT_ID = "ComposeTarget" // Hardwired into ComposeWindow
18+
19+
/**
20+
* A Skiko/Canvas-based top-level window using the browser's entire viewport. Supports resizing.
21+
* Taken from https://git.chrishatton.org/chris/homepage/-/blob/master/src/jsMain/kotlin/org/chrishatton/homepage/presentation/view/BrowserViewportWindow.kt
22+
* Credit to 'Oliver O' (https://github.com/OliverO2)
23+
*/
24+
fun BrowserViewportWindow(
25+
title: String = "Untitled",
26+
content: @Composable ComposeWindow.() -> Unit
27+
) {
28+
val htmlHeadElement = document.head!!
29+
htmlHeadElement.appendChild(
30+
(document.createElement("style") as HTMLStyleElement).apply {
31+
type = "text/css"
32+
appendChild(
33+
document.createTextNode(
34+
"""
35+
html, body {
36+
overflow: hidden;
37+
margin: 0 !important;
38+
padding: 0 !important;
39+
}
40+
41+
#$CANVAS_ELEMENT_ID {
42+
outline: none;
43+
}
44+
""".trimIndent()
45+
)
46+
)
47+
}
48+
)
49+
50+
fun HTMLCanvasElement.fillViewportSize() {
51+
setAttribute("width", "${window.innerWidth}")
52+
setAttribute("height", "${window.innerHeight}")
53+
}
54+
55+
val canvas = (document.getElementById(CANVAS_ELEMENT_ID) as HTMLCanvasElement).apply {
56+
fillViewportSize()
57+
}
58+
59+
ComposeWindow().apply {
60+
window.addEventListener("resize", {
61+
canvas.fillViewportSize()
62+
layer.layer.attachTo(canvas)
63+
layer.layer.needRedraw()
64+
layer.setSize(canvas.width, canvas.height)
65+
})
66+
67+
// WORKAROUND: ComposeWindow does not implement `setTitle(title)`
68+
val htmlTitleElement = (
69+
htmlHeadElement.getElementsByTagName("title").item(0)
70+
?: document.createElement("title").also { htmlHeadElement.appendChild(it) }
71+
) as HTMLTitleElement
72+
htmlTitleElement.textContent = title
73+
74+
setContent {
75+
content(this)
76+
}
77+
}
78+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>App</title>
6+
<script src="skiko.js"></script>
7+
<link href="styles.css" rel="stylesheet" type="text/css"/>
8+
</head>
9+
<body>
10+
<div id="ComposeTargetContainer">
11+
<canvas id="ComposeTarget"></canvas>
12+
<script src="app.js"></script>
13+
</div>
14+
</body>
15+
</html>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
html, body {
2+
height: 100%;
3+
margin: 0px;
4+
padding: 0px;
5+
}
6+
7+
canvas {
8+
width: 100vw;
9+
height: 100vh;
10+
display: block;
11+
position: fixed;
12+
top: 0;
13+
left: 0;
14+
}

0 commit comments

Comments
 (0)