-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
315 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,22 @@ | ||
<!doctype html> | ||
<!--suppress HtmlUnknownTarget --> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>MapLibre Compose demo app</title> | ||
<script src="skiko.js"></script> | ||
<link rel='stylesheet' href='https://unpkg.com/[email protected]/dist/maplibre-gl.css'/> | ||
<style> | ||
html, | ||
body { | ||
width: 100%; | ||
height: 100%; | ||
width: 100vw; | ||
height: 100vh; | ||
margin: 0; | ||
padding: 0; | ||
overflow: hidden; | ||
} | ||
</style> | ||
<script src="skiko.js"></script> | ||
</head> | ||
<body></body> | ||
<script src="app.js"></script> | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Module compose-html-interop | ||
|
||
Include an HTML element in a Compose Web UI. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
plugins { | ||
id("library-conventions") | ||
id(libs.plugins.kotlin.multiplatform.get().pluginId) | ||
id(libs.plugins.kotlin.composeCompiler.get().pluginId) | ||
id(libs.plugins.compose.get().pluginId) | ||
id(libs.plugins.mavenPublish.get().pluginId) | ||
} | ||
|
||
mavenPublishing { | ||
pom { | ||
name = "Compose HTML Interop" | ||
description = "Include an HTML element in a Compose Web UI." | ||
url = "https://github.com/sargunv/maplibre-compose" | ||
} | ||
} | ||
|
||
kotlin { | ||
js(IR) { browser() } | ||
|
||
sourceSets { | ||
commonMain.dependencies { | ||
implementation(kotlin("stdlib-js")) | ||
implementation(compose.foundation) | ||
} | ||
|
||
commonTest.dependencies { | ||
implementation(kotlin("test")) | ||
implementation(kotlin("test-common")) | ||
implementation(kotlin("test-annotations-common")) | ||
} | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlElement.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package dev.sargunv.composehtmlinterop | ||
|
||
import androidx.compose.foundation.layout.Box | ||
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 <T : HTMLElement> HtmlElement( | ||
factory: () -> T, | ||
update: (T) -> Unit = {}, | ||
modifier: Modifier = Modifier, | ||
) { | ||
val density = LocalDensity.current | ||
|
||
val container = | ||
rememberDomNode(parent = document.body!!) { | ||
document.createElement("div").unsafeCast<HTMLElement>().apply { | ||
style.position = "absolute" | ||
style.margin = "0px" | ||
} | ||
} | ||
|
||
val child = rememberDomNode(parent = container, factory = factory) | ||
|
||
SnapshotEffect(child) { update(it) } | ||
|
||
Box(modifier.onGloballyPositioned { container.matchLayout(it, density) }) | ||
|
||
HtmlFocusAdapter(container) | ||
} |
63 changes: 63 additions & 0 deletions
63
...ose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlFocusAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package dev.sargunv.composehtmlinterop | ||
|
||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.rememberUpdatedState | ||
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.focus.FocusDirection | ||
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) { | ||
val focusManager = LocalFocusManager.current | ||
var ownFocusRequest by remember { mutableStateOf(false) } | ||
|
||
val head = remember { FocusRequester() } | ||
val tail = remember { FocusRequester() } | ||
|
||
val currentContainer by rememberUpdatedState(container) | ||
|
||
Box( | ||
modifier = | ||
Modifier.focusRequester(head).onFocusChanged { | ||
if (it.isFocused && !ownFocusRequest) { | ||
val htmlHead = currentContainer.firstElementChild | ||
if (htmlHead != null) { | ||
focusManager.clearFocus(force = true) | ||
htmlHead.unsafeCast<HTMLElement>().focus() | ||
} else { | ||
ownFocusRequest = true | ||
tail.requestFocus() | ||
ownFocusRequest = false | ||
focusManager.moveFocus(FocusDirection.Next) | ||
} | ||
} | ||
} | ||
) | ||
|
||
Box( | ||
modifier = | ||
Modifier.focusRequester(tail).onFocusChanged { | ||
if (it.isFocused && !ownFocusRequest) { | ||
val htmlTail = currentContainer.lastElementChild | ||
if (htmlTail != null) { | ||
focusManager.clearFocus(force = true) | ||
htmlTail.unsafeCast<HTMLElement>().focus() | ||
} else { | ||
ownFocusRequest = true | ||
head.requestFocus() | ||
ownFocusRequest = false | ||
focusManager.moveFocus(FocusDirection.Previous) | ||
} | ||
} | ||
} | ||
) | ||
} |
23 changes: 23 additions & 0 deletions
23
...mpose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/SnapshotEffect.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package dev.sargunv.composehtmlinterop | ||
|
||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.DisposableEffect | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.rememberUpdatedState | ||
import androidx.compose.runtime.snapshots.SnapshotStateObserver | ||
|
||
@Composable | ||
internal fun <T : Any> SnapshotEffect(target: T, effect: (T) -> Unit) { | ||
val observer = remember { SnapshotStateObserver { it() } } | ||
val currentTarget by rememberUpdatedState(target) | ||
val currentEffect by rememberUpdatedState(effect) | ||
DisposableEffect(observer) { | ||
observer.start() | ||
observer.observeReads(Unit, { currentEffect(currentTarget) }) { currentEffect(currentTarget) } | ||
onDispose { | ||
observer.stop() | ||
observer.clear() | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/util.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
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 | ||
|
||
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 fun <T : Node> 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) } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
# Module maplibre-gl-js-kotlin | ||
|
||
Kotlin wrapper for [MapLibre GL JS](https://www.npmjs.com/package/maplibre-gl). | ||
Kotlin bindings for [MapLibre GL JS](https://www.npmjs.com/package/maplibre-gl). |
Oops, something went wrong.