|
| 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 | +} |
0 commit comments