diff --git a/.fleet/run.json b/.fleet/run.json index 823e91e1..e4c93de0 100644 --- a/.fleet/run.json +++ b/.fleet/run.json @@ -24,6 +24,14 @@ ":demo-app:run" ] }, + { + "name": "Js-App", + "type": "gradle", + "workingDir": "$PROJECT_DIR$", + "tasks": [ + ":demo-app:jsRun" + ] + }, { "name": "Reformat", "type": "gradle", diff --git a/README.md b/README.md index 92613e7a..dc19e3eb 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,11 @@ Android and iOS support is implemented with [MapLibre Native](https://github.com/maplibre/maplibre-native). A broad set of features are supported. -Desktop support is implemented with -[MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js) and -[KCEF](https://github.com/DatL4g/KCEF). It's currently **very** limited and -experimental. +Web support is implemented with +[MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js). + +Desktop support is also implemented with MapLibre GL JS and +[KCEF](https://github.com/DatL4g/KCEF) for now, though we'd like to switch to +MapLibre Native. Web is not yet supported. diff --git a/build.gradle.kts b/build.gradle.kts index 48c437a0..6efeef65 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,6 +48,8 @@ dependencies { dokka(project(":lib:maplibre-compose:")) dokka(project(":lib:maplibre-compose-expressions:")) dokka(project(":lib:maplibre-compose-material3:")) + dokka(project(":lib:kotlin-maplibre-js")) + dokka(project(":lib:compose-html-interop:")) } spotless { diff --git a/demo-app/build.gradle.kts b/demo-app/build.gradle.kts index 1fdde783..ccfb1610 100644 --- a/demo-app/build.gradle.kts +++ b/demo-app/build.gradle.kts @@ -47,6 +47,10 @@ kotlin { iosSimulatorArm64() iosX64() jvm("desktop") + js(IR) { + browser { commonWebpackConfig { outputFileName = "app.js" } } + binaries.executable() + } cocoapods { summary = "MapLibre Compose demo app" @@ -86,6 +90,7 @@ kotlin { androidMain.dependencies { implementation(libs.androidx.activity.compose) + implementation(libs.kotlinx.coroutines.android) implementation(libs.ktor.client.okhttp) } @@ -94,6 +99,12 @@ kotlin { desktopMain.dependencies { implementation(compose.desktop.currentOs) implementation(libs.kotlinx.coroutines.swing) + implementation(libs.ktor.client.okhttp) + } + + jsMain.dependencies { + implementation(compose.html.core) + implementation(libs.ktor.client.js) } commonTest.dependencies { diff --git a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/platform.kt b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/platform.kt deleted file mode 100644 index e1a6f007..00000000 --- a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/platform.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.sargunv.maplibrecompose.demoapp - -expect object Platform { - val supportsBlending: Boolean - val supportsFps: Boolean - val supportsCamera: Boolean - val supportsLayers: Boolean -} diff --git a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/util.kt b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/util.kt index d8996b55..c5e90f99 100644 --- a/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/util.kt +++ b/demo-app/src/commonMain/kotlin/dev/sargunv/maplibrecompose/demoapp/util.kt @@ -76,3 +76,10 @@ internal class FrameRateState(private val spinner: String = "◐◓◑◒") { } @Composable expect fun getDefaultColorScheme(isDark: Boolean = false): ColorScheme + +expect object Platform { + val supportsBlending: Boolean + val supportsFps: Boolean + val supportsCamera: Boolean + val supportsLayers: Boolean +} diff --git a/demo-app/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/demoapp/Main.kt b/demo-app/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/demoapp/Main.kt index ba1be0bd..e69e177b 100644 --- a/demo-app/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/demoapp/Main.kt +++ b/demo-app/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/demoapp/Main.kt @@ -30,3 +30,10 @@ fun main() { actual fun getDefaultColorScheme(isDark: Boolean): ColorScheme { return if (isDark) darkColorScheme() else lightColorScheme() } + +actual object Platform { + actual val supportsBlending = false + actual val supportsFps = false + actual val supportsCamera = false + actual val supportsLayers = false +} diff --git a/demo-app/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/demoapp/platform.desktop.kt b/demo-app/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/demoapp/platform.desktop.kt deleted file mode 100644 index 1f56fad6..00000000 --- a/demo-app/src/desktopMain/kotlin/dev/sargunv/maplibrecompose/demoapp/platform.desktop.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.sargunv.maplibrecompose.demoapp - -actual object Platform { - actual val supportsBlending = false - actual val supportsFps = false - actual val supportsCamera = false - actual val supportsLayers = false -} diff --git a/demo-app/src/iosMain/kotlin/dev/sargunv/maplibrecompose/demoapp/MainViewController.kt b/demo-app/src/iosMain/kotlin/dev/sargunv/maplibrecompose/demoapp/MainViewController.kt index 81c17bf2..1b26eba1 100644 --- a/demo-app/src/iosMain/kotlin/dev/sargunv/maplibrecompose/demoapp/MainViewController.kt +++ b/demo-app/src/iosMain/kotlin/dev/sargunv/maplibrecompose/demoapp/MainViewController.kt @@ -13,3 +13,10 @@ fun MainViewController() = ComposeUIViewController { DemoApp() } actual fun getDefaultColorScheme(isDark: Boolean): ColorScheme { return if (isDark) darkColorScheme() else lightColorScheme() } + +actual object Platform { + actual val supportsBlending = true + actual val supportsFps = true + actual val supportsCamera = true + actual val supportsLayers = true +} diff --git a/demo-app/src/iosMain/kotlin/dev/sargunv/maplibrecompose/demoapp/platform.ios.kt b/demo-app/src/iosMain/kotlin/dev/sargunv/maplibrecompose/demoapp/platform.ios.kt deleted file mode 100644 index ff33057d..00000000 --- a/demo-app/src/iosMain/kotlin/dev/sargunv/maplibrecompose/demoapp/platform.ios.kt +++ /dev/null @@ -1,8 +0,0 @@ -package dev.sargunv.maplibrecompose.demoapp - -actual object Platform { - actual val supportsBlending = true - actual val supportsFps = true - actual val supportsCamera = true - actual val supportsLayers = true -} diff --git a/demo-app/src/jsMain/kotlin/dev/sargunv/maplibrecompose/demoapp/main.kt b/demo-app/src/jsMain/kotlin/dev/sargunv/maplibrecompose/demoapp/main.kt new file mode 100644 index 00000000..8038121e --- /dev/null +++ b/demo-app/src/jsMain/kotlin/dev/sargunv/maplibrecompose/demoapp/main.kt @@ -0,0 +1,27 @@ +package dev.sargunv.maplibrecompose.demoapp + +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.ComposeViewport +import kotlinx.browser.document +import org.jetbrains.skiko.wasm.onWasmReady + +@OptIn(ExperimentalComposeUiApi::class) +fun main() { + onWasmReady { ComposeViewport(document.body!!) { DemoApp() } } +} + +@Composable +actual fun getDefaultColorScheme(isDark: Boolean): ColorScheme { + return if (isDark) darkColorScheme() else lightColorScheme() +} + +actual object Platform { + actual val supportsBlending = false + actual val supportsFps = false + actual val supportsCamera = false + actual val supportsLayers = false +} diff --git a/demo-app/src/jsMain/resources/index.html b/demo-app/src/jsMain/resources/index.html new file mode 100644 index 00000000..fcd23a78 --- /dev/null +++ b/demo-app/src/jsMain/resources/index.html @@ -0,0 +1,23 @@ + + + + + + + MapLibre Compose demo app + + + + + + + diff --git a/demo-app/src/jsMain/resources/styles.css b/demo-app/src/jsMain/resources/styles.css new file mode 100644 index 00000000..8e94d43f --- /dev/null +++ b/demo-app/src/jsMain/resources/styles.css @@ -0,0 +1,7 @@ +html, body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; +} diff --git a/docs/docs/index.md b/docs/docs/index.md index 9a537c24..279d9b4e 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -21,29 +21,31 @@ to express an interactive map API in Compose. Android and iOS support is implemented with [MapLibre Native][maplibre-native]. -Desktop support is implemented with [MapLibre GL JS][maplibre-js] and -[KCEF][kcef]. - -| Feature | Android | iOS | Desktop | Web | -| ------------------------------------------------- | ------------------ | ------------------ | ------------------ | --- | -| Render a map | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | -| Overlay Compose UI over the map | :white_check_mark: | :white_check_mark: | :x: | :x: | -| Load Compose resource URIs | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | -| Configure ornaments (compass, logo, attribution) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | -| Configure gestures (pan, zoom, rotate, pitch) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | -| Respond to a map click or long click | :white_check_mark: | :white_check_mark: | :x: | :x: | -| Query visible map features | :white_check_mark: | :white_check_mark: | :x: | :x: | -| Get, set, and animate the camera position | :white_check_mark: | :white_check_mark: | :x: | :x: | -| Convert between screen and geographic coordinates | :white_check_mark: | :white_check_mark: | :x: | :x: | -| Get the currently visible region and bounding box | :white_check_mark: | :white_check_mark: | :x: | :x: | -| Insert, remove, and replace layers | :white_check_mark: | :white_check_mark: | :x: | :x: | -| Configure layers with expressions | :white_check_mark: | :white_check_mark: | :x: | :x: | -| Add data sources by URI or GeoJSON | :white_check_mark: | :white_check_mark: | :x: | :x: | -| Add images to the style | :white_check_mark: | :white_check_mark: | :x: | :x: | -| Add annotations | :x: | :x: | :x: | :x: | -| Snapshot the map as an image | :x: | :x: | :x: | :x: | -| Configure the offline cache | :x: | :x: | :x: | :x: | -| Configure layer transitions | :x: | :x: | :x: | :x: | +Web support is implemented with [MapLibre GL JS][maplibre-js]. + +Desktop support is also implemented with MapLibre GL JS in [KCEF][kcef] for now, +though we'd like to switch to MapLibre Native. + +| Feature | Android | iOS | Desktop | Web | +| ------------------------------------------------- | ------------------ | ------------------ | ------------------ | ------------------ | +| Render a map | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Overlay Compose UI over the map | :white_check_mark: | :white_check_mark: | :x: | :x: | +| Load Compose resource URIs | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | +| Configure ornaments (compass, logo, attribution) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | +| Configure gestures (pan, zoom, rotate, pitch) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: | +| Respond to a map click or long click | :white_check_mark: | :white_check_mark: | :x: | :x: | +| Query visible map features | :white_check_mark: | :white_check_mark: | :x: | :x: | +| Get, set, and animate the camera position | :white_check_mark: | :white_check_mark: | :x: | :x: | +| Convert between screen and geographic coordinates | :white_check_mark: | :white_check_mark: | :x: | :x: | +| Get the currently visible region and bounding box | :white_check_mark: | :white_check_mark: | :x: | :x: | +| Insert, remove, and replace layers | :white_check_mark: | :white_check_mark: | :x: | :x: | +| Configure layers with expressions | :white_check_mark: | :white_check_mark: | :x: | :x: | +| Add data sources by URI or GeoJSON | :white_check_mark: | :white_check_mark: | :x: | :x: | +| Add images to the style | :white_check_mark: | :white_check_mark: | :x: | :x: | +| Add annotations | :x: | :x: | :x: | :x: | +| Snapshot the map as an image | :x: | :x: | :x: | :x: | +| Configure the offline cache | :x: | :x: | :x: | :x: | +| Configure layer transitions | :x: | :x: | :x: | :x: | [compose]: https://www.jetbrains.com/compose-multiplatform/ [maplibre]: https://maplibre.org/ diff --git a/gradle.properties b/gradle.properties index c20a93e7..33845fea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,6 +4,8 @@ androidTargetSdk=35 iosDeploymentTarget=12.0 kotlin.code.style=official kotlin.daemon.jvmargs=-Xmx2048M +kotlin.incremental.wasm=true +org.jetbrains.compose.experimental.jscanvas.enabled=true org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true # Blocker: https://github.com/diffplug/spotless/issues/2347 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 48e95c28..4cac7983 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,8 +2,8 @@ androidx-activity = "1.9.3" androidx-composeUi = "1.7.6" androidx-navigation = "2.8.0-alpha11" -webview = "1.9.40" kermit = "2.0.5" +kotlinx-browser = "0.3" kotlinx-coroutines = "1.9.0" ktor = "3.0.3" maplibre-android-sdk = "11.7.1" @@ -13,6 +13,7 @@ maplibre-js = "4.7.1" spatialk = "0.3.0" webpack-html = "5.6.3" webpack-htmlInlineScript = "3.2.1" +webview = "1.9.40" gradle-android = "8.7.3" gradle-compose = "1.7.3" @@ -29,11 +30,14 @@ tool-prettier = "3.4.2" androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } androidx-composeUi-testManifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "androidx-composeUi" } androidx-navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "androidx-navigation" } +kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser", version.ref = "kotlinx-browser" } webview = { module = "io.github.kevinnzou:compose-webview-multiplatform", version.ref = "webview" } kermit = { group = "co.touchlab", name = "kermit", version.ref = "kermit" } +kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" } ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } +ktor-client-js = { module = "io.ktor:ktor-client-js", version.ref = "ktor" } ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } ktor-client-contentNegotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } ktor-serialization-kotlinxJson = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index 28fe7069..caf6859b 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -112,7 +112,7 @@ B92378962B6B1156000C7307 /* Frameworks */, 7555FF79242A565900829871 /* Resources */, FB29095E4E4AE33FC6A4F030 /* [CP] Embed Pods Frameworks */, - E8A0EF8E3B4DDB502C870BB0 /* [CP] Copy Pods Resources */, + 1DBF57571B44936782348BCD /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -170,43 +170,43 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 81EA16B41DBA59C4915ABE3F /* [CP] Check Pods Manifest.lock */ = { + 1DBF57571B44936782348BCD /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-iosApp-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources.sh\"\n"; showEnvVarsInLog = 0; }; - E8A0EF8E3B4DDB502C870BB0 /* [CP] Copy Pods Resources */ = { + 81EA16B41DBA59C4915ABE3F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-iosApp-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-iosApp/Pods-iosApp-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; FB29095E4E4AE33FC6A4F030 /* [CP] Embed Pods Frameworks */ = { diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index b8387491..76a2f89f 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -52,6 +52,11 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@js-joda/core@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" + integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.5" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz#4fc56c15c580b9adb7dc3c333a134e540b44bfb1" @@ -3288,7 +3293,7 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@^8.13.0: +ws@8.18.0, ws@^8.13.0: version "8.18.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== diff --git a/lib/compose-html-interop/MODULE.md b/lib/compose-html-interop/MODULE.md new file mode 100644 index 00000000..a667366d --- /dev/null +++ b/lib/compose-html-interop/MODULE.md @@ -0,0 +1,3 @@ +# Module compose-html-interop + +Include an HTML element in a Compose Web UI. diff --git a/lib/compose-html-interop/build.gradle.kts b/lib/compose-html-interop/build.gradle.kts new file mode 100644 index 00000000..514d8635 --- /dev/null +++ b/lib/compose-html-interop/build.gradle.kts @@ -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")) + } + } +} 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 new file mode 100644 index 00000000..084758e2 --- /dev/null +++ b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlElement.kt @@ -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 HtmlElement( + factory: () -> T, + update: (T) -> Unit = {}, + 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 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 new file mode 100644 index 00000000..0ae1f99a --- /dev/null +++ b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/HtmlFocusAdapter.kt @@ -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().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().focus() + } else { + ownFocusRequest = true + head.requestFocus() + ownFocusRequest = false + focusManager.moveFocus(FocusDirection.Previous) + } + } + } + ) +} diff --git a/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/SnapshotEffect.kt b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/SnapshotEffect.kt new file mode 100644 index 00000000..54319789 --- /dev/null +++ b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/SnapshotEffect.kt @@ -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 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() + } + } +} 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 new file mode 100644 index 00000000..5c5bf96c --- /dev/null +++ b/lib/compose-html-interop/src/commonMain/kotlin/dev/sargunv/composehtmlinterop/util.kt @@ -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 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) } + } + } +} diff --git a/lib/kotlin-maplibre-js/MODULE.md b/lib/kotlin-maplibre-js/MODULE.md index cf75d481..3e9d3c20 100644 --- a/lib/kotlin-maplibre-js/MODULE.md +++ b/lib/kotlin-maplibre-js/MODULE.md @@ -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). diff --git a/lib/kotlin-maplibre-js/build.gradle.kts b/lib/kotlin-maplibre-js/build.gradle.kts index 6ba5ba63..31e2dde1 100644 --- a/lib/kotlin-maplibre-js/build.gradle.kts +++ b/lib/kotlin-maplibre-js/build.gradle.kts @@ -13,7 +13,7 @@ mavenPublishing { } kotlin { - js(IR) { browser {} } + js(IR) { browser() } sourceSets { commonMain.dependencies { diff --git a/lib/kotlin-maplibre-js/src/commonMain/kotlin/dev/sargunv/maplibrejs/external.kt b/lib/kotlin-maplibre-js/src/commonMain/kotlin/dev/sargunv/maplibrejs/external.kt index 2927955c..dd8f7766 100644 --- a/lib/kotlin-maplibre-js/src/commonMain/kotlin/dev/sargunv/maplibrejs/external.kt +++ b/lib/kotlin-maplibre-js/src/commonMain/kotlin/dev/sargunv/maplibrejs/external.kt @@ -2,11 +2,11 @@ package dev.sargunv.maplibrejs +import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.HTMLElement /** [Map](https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/) */ -@JsName("Map") -public external class Maplibre public constructor(options: MapOptions) { +public external class Map public constructor(options: MapOptions) { public var repaint: Boolean public var showCollisionBoxes: Boolean public var showOverdrawInspector: Boolean @@ -57,6 +57,14 @@ public external class Maplibre public constructor(options: MapOptions) { public fun addControl(control: IControl, position: String) public fun removeControl(control: IControl) + + public fun triggerRepaint() + + public fun getCanvasContainer(): HTMLElement + + public fun getCanvas(): HTMLCanvasElement + + public fun resize() } /** @@ -157,9 +165,9 @@ public external class TwoFingersTouchZoomRotateHandler { /** [LogoControl](https://maplibre.org/maplibre-gl-js/docs/API/classes/LogoControl/) */ public external class LogoControl public constructor(options: LogoControlOptions = definedExternally) : IControl { - override fun onAdd(map: Maplibre): HTMLElement + override fun onAdd(map: Map): HTMLElement - override fun onRemove(map: Maplibre) + override fun onRemove(map: Map) } /** @@ -172,9 +180,9 @@ public external interface LogoControlOptions { /** [ScaleControl](https://maplibre.org/maplibre-gl-js/docs/API/classes/ScaleControl/) */ public external class ScaleControl public constructor(options: ScaleControlOptions = definedExternally) : IControl { - override fun onAdd(map: Maplibre): HTMLElement + override fun onAdd(map: Map): HTMLElement - override fun onRemove(map: Maplibre) + override fun onRemove(map: Map) } /** @@ -190,9 +198,9 @@ public external interface ScaleControlOptions { */ public external class AttributionControl public constructor(options: AttributionControlOptions = definedExternally) : IControl { - override fun onAdd(map: Maplibre): HTMLElement + override fun onAdd(map: Map): HTMLElement - override fun onRemove(map: Maplibre) + override fun onRemove(map: Map) } /** @@ -206,9 +214,9 @@ public external interface AttributionControlOptions { /** [NavigationControl](https://maplibre.org/maplibre-gl-js/docs/API/classes/NavigationControl/) */ public external class NavigationControl public constructor(options: NavigationControlOptions = definedExternally) : IControl { - override fun onAdd(map: Maplibre): HTMLElement + override fun onAdd(map: Map): HTMLElement - override fun onRemove(map: Maplibre) + override fun onRemove(map: Map) } /** @@ -222,9 +230,9 @@ public external interface NavigationControlOptions { /** [IControl](https://maplibre.org/maplibre-gl-js/docs/API/interfaces/IControl/) */ public external interface IControl { - public fun onAdd(map: Maplibre): HTMLElement + public fun onAdd(map: Map): HTMLElement - public fun onRemove(map: Maplibre) + public fun onRemove(map: Map) } /** [LngLat](https://maplibre.org/maplibre-gl-js/docs/API/classes/LngLat/) */ diff --git a/lib/maplibre-compose-expressions/build.gradle.kts b/lib/maplibre-compose-expressions/build.gradle.kts index 2110ff69..f1029b41 100644 --- a/lib/maplibre-compose-expressions/build.gradle.kts +++ b/lib/maplibre-compose-expressions/build.gradle.kts @@ -1,7 +1,7 @@ -@file:OptIn(ExperimentalKotlinGradlePluginApi::class, ExperimentalComposeLibrary::class) +@file:OptIn(ExperimentalKotlinGradlePluginApi::class, ExperimentalWasmDsl::class) -import org.jetbrains.compose.ExperimentalComposeLibrary import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree @@ -35,6 +35,8 @@ kotlin { iosSimulatorArm64() iosX64() jvm("desktop") + js(IR) { browser() } + wasmJs { browser() } sourceSets { commonMain.dependencies { implementation(compose.foundation) } diff --git a/lib/maplibre-compose-material3/build.gradle.kts b/lib/maplibre-compose-material3/build.gradle.kts index 92a10663..76a3750f 100644 --- a/lib/maplibre-compose-material3/build.gradle.kts +++ b/lib/maplibre-compose-material3/build.gradle.kts @@ -36,6 +36,7 @@ kotlin { iosSimulatorArm64() iosX64() jvm("desktop") + js(IR) { browser() } cocoapods { noPodspec() diff --git a/lib/maplibre-compose-webview/build.gradle.kts b/lib/maplibre-compose-webview/build.gradle.kts index 1989e309..c8e59fd3 100644 --- a/lib/maplibre-compose-webview/build.gradle.kts +++ b/lib/maplibre-compose-webview/build.gradle.kts @@ -6,7 +6,6 @@ plugins { kotlin { js(IR) { browser { webpackTask {} } - useEsModules() binaries.executable() generateTypeScriptDefinitions() diff --git a/lib/maplibre-compose-webview/src/commonMain/kotlin/dev/sargunv/maplibrecompose/webview/WebviewMapBridge.kt b/lib/maplibre-compose-webview/src/commonMain/kotlin/dev/sargunv/maplibrecompose/webview/WebviewMapBridge.kt index ca556311..7c6ab1e9 100644 --- a/lib/maplibre-compose-webview/src/commonMain/kotlin/dev/sargunv/maplibrecompose/webview/WebviewMapBridge.kt +++ b/lib/maplibre-compose-webview/src/commonMain/kotlin/dev/sargunv/maplibrecompose/webview/WebviewMapBridge.kt @@ -5,8 +5,8 @@ package dev.sargunv.maplibrecompose.webview import dev.sargunv.maplibrejs.AttributionControl import dev.sargunv.maplibrejs.LogoControl +import dev.sargunv.maplibrejs.Map import dev.sargunv.maplibrejs.MapOptions -import dev.sargunv.maplibrejs.Maplibre import dev.sargunv.maplibrejs.NavigationControl import dev.sargunv.maplibrejs.NavigationControlOptions import dev.sargunv.maplibrejs.ScaleControl @@ -16,7 +16,7 @@ import org.w3c.dom.HTMLDivElement @JsExport object WebviewMapBridge { private var container: HTMLDivElement? = null - private lateinit var map: Maplibre + private lateinit var map: Map private lateinit var navigationControl: NavigationControl private lateinit var logoControl: LogoControl private lateinit var scaleControl: ScaleControl @@ -28,7 +28,7 @@ object WebviewMapBridge { it.setAttribute("style", "width: 100%; height: 100vh;") document.body!!.appendChild(it) } as HTMLDivElement - map = Maplibre(MapOptions(container = container!!, disableAttributionControl = true)) + map = Map(MapOptions(container = container!!, disableAttributionControl = true)) navigationControl = NavigationControl(NavigationControlOptions(visualizePitch = true)) logoControl = LogoControl() scaleControl = ScaleControl() diff --git a/lib/maplibre-compose/build.gradle.kts b/lib/maplibre-compose/build.gradle.kts index c29f8ac5..baa31416 100644 --- a/lib/maplibre-compose/build.gradle.kts +++ b/lib/maplibre-compose/build.gradle.kts @@ -55,6 +55,7 @@ kotlin { iosSimulatorArm64() iosX64() jvm("desktop") + js(IR) { browser() } cocoapods { noPodspec() @@ -88,6 +89,11 @@ kotlin { implementation(libs.webview) } + jsMain.dependencies { + implementation(project(":lib:kotlin-maplibre-js")) + implementation(project(":lib:compose-html-interop")) + } + commonTest.dependencies { implementation(kotlin("test")) implementation(kotlin("test-common")) @@ -110,9 +116,6 @@ compose.resources { customDirectory( sourceSetName = "desktopMain", - directoryProvider = - // layout.dir(copyDesktopResources.map { - // it.destinationDir.relativeTo(layout.projectDirectory.asFile) }), - layout.dir(copyDesktopResources.map { it.destinationDir }), + directoryProvider = layout.dir(copyDesktopResources.map { it.destinationDir }), ) } diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/compose/WebMapView.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/compose/WebMapView.kt new file mode 100644 index 00000000..7cd631f9 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/compose/WebMapView.kt @@ -0,0 +1,66 @@ +package dev.sargunv.maplibrecompose.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onGloballyPositioned +import co.touchlab.kermit.Logger +import dev.sargunv.composehtmlinterop.HtmlElement +import dev.sargunv.maplibrecompose.core.MaplibreMap +import dev.sargunv.maplibrejs.Map +import dev.sargunv.maplibrejs.MapOptions +import kotlinx.browser.document +import org.w3c.dom.HTMLElement + +@Composable +internal actual fun ComposableMapView( + modifier: Modifier, + styleUri: String, + update: (map: MaplibreMap) -> Unit, + onReset: () -> Unit, + logger: Logger?, + callbacks: MaplibreMap.Callbacks, +) = + WebMapView( + modifier = modifier, + styleUri = styleUri, + update = update, + onReset = onReset, + logger = logger, + callbacks = callbacks, + ) + +@Composable +internal fun WebMapView( + modifier: Modifier, + styleUri: String, + update: (map: MaplibreMap) -> Unit, + onReset: () -> Unit, + logger: Logger?, + callbacks: MaplibreMap.Callbacks, +) { + var maybeMap by remember { mutableStateOf(null) } + + HtmlElement( + modifier = modifier.onGloballyPositioned { maybeMap?.resize() }, + factory = { + document.createElement("div").unsafeCast().apply { + style.apply { + width = "100%" + height = "100%" + } + } + }, + update = { element -> + val map = + maybeMap + ?: Map(MapOptions(container = element, disableAttributionControl = true)).also { + maybeMap = it + } + map.setStyle(styleUri) + }, + ) +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt new file mode 100644 index 00000000..7024e0eb --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/BackgroundLayer.kt @@ -0,0 +1,23 @@ +package dev.sargunv.maplibrecompose.core.layer + +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue + +internal actual class BackgroundLayer actual constructor(id: String) : Layer() { + + override val impl: Nothing = TODO() + + actual fun setBackgroundColor(color: CompiledExpression) { + TODO() + } + + actual fun setBackgroundPattern(pattern: CompiledExpression) { + TODO() + } + + actual fun setBackgroundOpacity(opacity: CompiledExpression) { + TODO() + } +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt new file mode 100644 index 00000000..65d63238 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/CircleLayer.kt @@ -0,0 +1,71 @@ +package dev.sargunv.maplibrecompose.core.layer + +import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.CirclePitchScale +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor + +internal actual class CircleLayer actual constructor(id: String, source: Source) : + FeatureLayer(source) { + override val impl = TODO() + + actual override var sourceLayer: String = TODO() + + actual override fun setFilter(filter: CompiledExpression) { + TODO() + } + + actual fun setCircleSortKey(sortKey: CompiledExpression) { + TODO() + } + + actual fun setCircleRadius(radius: CompiledExpression) { + TODO() + } + + actual fun setCircleColor(color: CompiledExpression) { + TODO() + } + + actual fun setCircleBlur(blur: CompiledExpression) { + TODO() + } + + actual fun setCircleOpacity(opacity: CompiledExpression) { + TODO() + } + + actual fun setCircleTranslate(translate: CompiledExpression) { + TODO() + } + + actual fun setCircleTranslateAnchor(translateAnchor: CompiledExpression) { + TODO() + } + + actual fun setCirclePitchScale(pitchScale: CompiledExpression) { + TODO() + } + + actual fun setCirclePitchAlignment(pitchAlignment: CompiledExpression) { + TODO() + } + + actual fun setCircleStrokeWidth(strokeWidth: CompiledExpression) { + TODO() + } + + actual fun setCircleStrokeColor(strokeColor: CompiledExpression) { + TODO() + } + + actual fun setCircleStrokeOpacity(strokeOpacity: CompiledExpression) { + TODO() + } +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt new file mode 100644 index 00000000..0e08ea0e --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FeatureLayer.kt @@ -0,0 +1,11 @@ +package dev.sargunv.maplibrecompose.core.layer + +import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue + +internal actual sealed class FeatureLayer actual constructor(actual val source: Source) : Layer() { + actual abstract var sourceLayer: String + + actual abstract fun setFilter(filter: CompiledExpression) +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt new file mode 100644 index 00000000..a4b9273b --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillExtrusionLayer.kt @@ -0,0 +1,53 @@ +package dev.sargunv.maplibrecompose.core.layer + +import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor + +internal actual class FillExtrusionLayer actual constructor(id: String, source: Source) : + FeatureLayer(source) { + override val impl = TODO() + + actual override var sourceLayer: String = TODO() + + actual override fun setFilter(filter: CompiledExpression) { + TODO() + } + + actual fun setFillExtrusionOpacity(opacity: CompiledExpression) { + TODO() + } + + actual fun setFillExtrusionColor(color: CompiledExpression) { + TODO() + } + + actual fun setFillExtrusionTranslate(translate: CompiledExpression) { + TODO() + } + + actual fun setFillExtrusionTranslateAnchor(anchor: CompiledExpression) { + TODO() + } + + actual fun setFillExtrusionPattern(pattern: CompiledExpression) { + TODO() + } + + actual fun setFillExtrusionHeight(height: CompiledExpression) { + TODO() + } + + actual fun setFillExtrusionBase(base: CompiledExpression) { + TODO() + } + + actual fun setFillExtrusionVerticalGradient(verticalGradient: CompiledExpression) { + TODO() + } +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt new file mode 100644 index 00000000..6089fd52 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/FillLayer.kt @@ -0,0 +1,54 @@ +package dev.sargunv.maplibrecompose.core.layer + +import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor + +internal actual class FillLayer actual constructor(id: String, source: Source) : + FeatureLayer(source) { + + override val impl = TODO() + + actual override var sourceLayer: String = TODO() + + actual override fun setFilter(filter: CompiledExpression) { + TODO() + } + + actual fun setFillSortKey(sortKey: CompiledExpression) { + TODO() + } + + actual fun setFillAntialias(antialias: CompiledExpression) { + TODO() + } + + actual fun setFillOpacity(opacity: CompiledExpression) { + TODO() + } + + actual fun setFillColor(color: CompiledExpression) { + TODO() + } + + actual fun setFillOutlineColor(outlineColor: CompiledExpression) { + TODO() + } + + actual fun setFillTranslate(translate: CompiledExpression) { + TODO() + } + + actual fun setFillTranslateAnchor(translateAnchor: CompiledExpression) { + TODO() + } + + actual fun setFillPattern(pattern: CompiledExpression) { + TODO() + } +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt new file mode 100644 index 00000000..64bde753 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HeatmapLayer.kt @@ -0,0 +1,39 @@ +package dev.sargunv.maplibrecompose.core.layer + +import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue + +internal actual class HeatmapLayer actual constructor(id: String, source: Source) : + FeatureLayer(source) { + override val impl = TODO() + + actual override var sourceLayer: String = TODO() + + actual override fun setFilter(filter: CompiledExpression) { + TODO() + } + + actual fun setHeatmapRadius(radius: CompiledExpression) { + TODO() + } + + actual fun setHeatmapWeight(weight: CompiledExpression) { + TODO() + } + + actual fun setHeatmapIntensity(intensity: CompiledExpression) { + TODO() + } + + actual fun setHeatmapColor(color: CompiledExpression) { + TODO() + } + + actual fun setHeatmapOpacity(opacity: CompiledExpression) { + TODO() + } +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt new file mode 100644 index 00000000..709a61a9 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/HillshadeLayer.kt @@ -0,0 +1,36 @@ +package dev.sargunv.maplibrecompose.core.layer + +import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.IlluminationAnchor + +internal actual class HillshadeLayer actual constructor(id: String, actual val source: Source) : + Layer() { + override val impl = TODO() + + actual fun setHillshadeIlluminationDirection(direction: CompiledExpression) { + TODO() + } + + actual fun setHillshadeIlluminationAnchor(anchor: CompiledExpression) { + TODO() + } + + actual fun setHillshadeExaggeration(exaggeration: CompiledExpression) { + TODO() + } + + actual fun setHillshadeShadowColor(shadowColor: CompiledExpression) { + TODO() + } + + actual fun setHillshadeHighlightColor(highlightColor: CompiledExpression) { + TODO() + } + + actual fun setHillshadeAccentColor(accentColor: CompiledExpression) { + TODO() + } +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/Layer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/Layer.kt new file mode 100644 index 00000000..9479573b --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/Layer.kt @@ -0,0 +1,28 @@ +package dev.sargunv.maplibrecompose.core.layer + +internal actual sealed class Layer { + abstract val impl: Nothing + + actual val id: String + get() = TODO() + + actual var minZoom: Float + get() = TODO() + set(value) { + TODO() + } + + actual var maxZoom: Float + get() = TODO() + set(value) { + TODO() + } + + actual var visible: Boolean + get() = TODO() + set(value) { + TODO() + } + + override fun toString() = "${this::class.simpleName}(id=\"$id\")" +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt new file mode 100644 index 00000000..0737ccfa --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/LineLayer.kt @@ -0,0 +1,90 @@ +package dev.sargunv.maplibrecompose.core.layer + +import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.LineCap +import dev.sargunv.maplibrecompose.expressions.value.LineJoin +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor +import dev.sargunv.maplibrecompose.expressions.value.VectorValue + +internal actual class LineLayer actual constructor(id: String, source: Source) : + FeatureLayer(source) { + + override val impl = TODO() + + actual override var sourceLayer: String = TODO() + + actual override fun setFilter(filter: CompiledExpression) { + TODO() + } + + actual fun setLineCap(cap: CompiledExpression) { + TODO() + } + + actual fun setLineJoin(join: CompiledExpression) { + TODO() + } + + actual fun setLineMiterLimit(miterLimit: CompiledExpression) { + TODO() + } + + actual fun setLineRoundLimit(roundLimit: CompiledExpression) { + TODO() + } + + actual fun setLineSortKey(sortKey: CompiledExpression) { + TODO() + } + + actual fun setLineOpacity(opacity: CompiledExpression) { + TODO() + } + + actual fun setLineColor(color: CompiledExpression) { + TODO() + } + + actual fun setLineTranslate(translate: CompiledExpression) { + TODO() + } + + actual fun setLineTranslateAnchor(translateAnchor: CompiledExpression) { + TODO() + } + + actual fun setLineWidth(width: CompiledExpression) { + TODO() + } + + actual fun setLineGapWidth(gapWidth: CompiledExpression) { + TODO() + } + + actual fun setLineOffset(offset: CompiledExpression) { + TODO() + } + + actual fun setLineBlur(blur: CompiledExpression) { + TODO() + } + + actual fun setLineDasharray(dasharray: CompiledExpression>) { + TODO() + } + + actual fun setLinePattern(pattern: CompiledExpression) { + TODO() + } + + actual fun setLineGradient(gradient: CompiledExpression) { + TODO() + } +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt new file mode 100644 index 00000000..eef06e8f --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/RasterLayer.kt @@ -0,0 +1,44 @@ +package dev.sargunv.maplibrecompose.core.layer + +import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.MillisecondsValue +import dev.sargunv.maplibrecompose.expressions.value.RasterResampling + +internal actual class RasterLayer actual constructor(id: String, actual val source: Source) : + Layer() { + override val impl = TODO() + + actual fun setRasterOpacity(opacity: CompiledExpression) { + TODO() + } + + actual fun setRasterHueRotate(hueRotate: CompiledExpression) { + TODO() + } + + actual fun setRasterBrightnessMin(brightnessMin: CompiledExpression) { + TODO() + } + + actual fun setRasterBrightnessMax(brightnessMax: CompiledExpression) { + TODO() + } + + actual fun setRasterSaturation(saturation: CompiledExpression) { + TODO() + } + + actual fun setRasterContrast(contrast: CompiledExpression) { + TODO() + } + + actual fun setRasterResampling(resampling: CompiledExpression) { + TODO() + } + + actual fun setRasterFadeDuration(fadeDuration: CompiledExpression) { + TODO() + } +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt new file mode 100644 index 00000000..af047326 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/SymbolLayer.kt @@ -0,0 +1,277 @@ +package dev.sargunv.maplibrecompose.core.layer + +import dev.sargunv.maplibrecompose.core.source.Source +import dev.sargunv.maplibrecompose.expressions.ast.CompiledExpression +import dev.sargunv.maplibrecompose.expressions.value.BooleanValue +import dev.sargunv.maplibrecompose.expressions.value.ColorValue +import dev.sargunv.maplibrecompose.expressions.value.DpOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.DpPaddingValue +import dev.sargunv.maplibrecompose.expressions.value.DpValue +import dev.sargunv.maplibrecompose.expressions.value.FloatOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.FloatValue +import dev.sargunv.maplibrecompose.expressions.value.FormattedValue +import dev.sargunv.maplibrecompose.expressions.value.IconPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.IconTextFit +import dev.sargunv.maplibrecompose.expressions.value.ImageValue +import dev.sargunv.maplibrecompose.expressions.value.ListValue +import dev.sargunv.maplibrecompose.expressions.value.StringValue +import dev.sargunv.maplibrecompose.expressions.value.SymbolAnchor +import dev.sargunv.maplibrecompose.expressions.value.SymbolPlacement +import dev.sargunv.maplibrecompose.expressions.value.SymbolZOrder +import dev.sargunv.maplibrecompose.expressions.value.TextJustify +import dev.sargunv.maplibrecompose.expressions.value.TextPitchAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextRotationAlignment +import dev.sargunv.maplibrecompose.expressions.value.TextTransform +import dev.sargunv.maplibrecompose.expressions.value.TextVariableAnchorOffsetValue +import dev.sargunv.maplibrecompose.expressions.value.TextWritingMode +import dev.sargunv.maplibrecompose.expressions.value.TranslateAnchor + +internal actual class SymbolLayer actual constructor(id: String, source: Source) : + FeatureLayer(source) { + override val impl = TODO() + + actual override var sourceLayer: String = TODO() + + actual override fun setFilter(filter: CompiledExpression) { + TODO() + } + + actual fun setSymbolPlacement(placement: CompiledExpression) { + TODO() + } + + actual fun setSymbolSpacing(spacing: CompiledExpression) { + TODO() + } + + actual fun setSymbolAvoidEdges(avoidEdges: CompiledExpression) { + TODO() + } + + actual fun setSymbolSortKey(sortKey: CompiledExpression) { + TODO() + } + + actual fun setSymbolZOrder(zOrder: CompiledExpression) { + TODO() + } + + actual fun setIconAllowOverlap(allowOverlap: CompiledExpression) { + TODO() + } + + actual fun setIconOverlap(overlap: CompiledExpression) { + TODO() + } + + actual fun setIconIgnorePlacement(ignorePlacement: CompiledExpression) { + TODO() + } + + actual fun setIconOptional(optional: CompiledExpression) { + TODO() + } + + actual fun setIconRotationAlignment( + rotationAlignment: CompiledExpression + ) { + TODO() + } + + actual fun setIconSize(size: CompiledExpression) { + TODO() + } + + actual fun setIconTextFit(textFit: CompiledExpression) { + TODO() + } + + actual fun setIconTextFitPadding(textFitPadding: CompiledExpression) { + TODO() + } + + actual fun setIconImage(image: CompiledExpression) { + TODO() + } + + actual fun setIconRotate(rotate: CompiledExpression) { + TODO() + } + + actual fun setIconPadding(padding: CompiledExpression) { + TODO() + } + + actual fun setIconKeepUpright(keepUpright: CompiledExpression) { + TODO() + } + + actual fun setIconOffset(offset: CompiledExpression) { + TODO() + } + + actual fun setIconAnchor(anchor: CompiledExpression) { + TODO() + } + + actual fun setIconPitchAlignment(pitchAlignment: CompiledExpression) { + TODO() + } + + actual fun setIconOpacity(opacity: CompiledExpression) { + TODO() + } + + actual fun setIconColor(color: CompiledExpression) { + TODO() + } + + actual fun setIconHaloColor(haloColor: CompiledExpression) { + TODO() + } + + actual fun setIconHaloWidth(haloWidth: CompiledExpression) { + TODO() + } + + actual fun setIconHaloBlur(haloBlur: CompiledExpression) { + TODO() + } + + actual fun setIconTranslate(translate: CompiledExpression) { + TODO() + } + + actual fun setIconTranslateAnchor(translateAnchor: CompiledExpression) { + TODO() + } + + actual fun setTextPitchAlignment(pitchAlignment: CompiledExpression) { + TODO() + } + + actual fun setTextRotationAlignment( + rotationAlignment: CompiledExpression + ) { + TODO() + } + + actual fun setTextField(field: CompiledExpression) { + TODO() + } + + actual fun setTextFont(font: CompiledExpression>) { + TODO() + } + + actual fun setTextSize(size: CompiledExpression) { + TODO() + } + + actual fun setTextMaxWidth(maxWidth: CompiledExpression) { + TODO() + } + + actual fun setTextLineHeight(lineHeight: CompiledExpression) { + TODO() + } + + actual fun setTextLetterSpacing(letterSpacing: CompiledExpression) { + TODO() + } + + actual fun setTextJustify(justify: CompiledExpression) { + TODO() + } + + actual fun setTextRadialOffset(radialOffset: CompiledExpression) { + TODO() + } + + actual fun setTextVariableAnchor(variableAnchor: CompiledExpression>) { + TODO() + } + + actual fun setTextVariableAnchorOffset( + variableAnchorOffset: CompiledExpression + ) { + TODO() + } + + actual fun setTextAnchor(anchor: CompiledExpression) { + TODO() + } + + actual fun setTextMaxAngle(maxAngle: CompiledExpression) { + TODO() + } + + actual fun setTextWritingMode(writingMode: CompiledExpression>) { + TODO() + } + + actual fun setTextRotate(rotate: CompiledExpression) { + TODO() + } + + actual fun setTextPadding(padding: CompiledExpression) { + TODO() + } + + actual fun setTextKeepUpright(keepUpright: CompiledExpression) { + TODO() + } + + actual fun setTextTransform(transform: CompiledExpression) { + TODO() + } + + actual fun setTextOffset(offset: CompiledExpression) { + TODO() + } + + actual fun setTextAllowOverlap(allowOverlap: CompiledExpression) { + TODO() + } + + actual fun setTextOverlap(overlap: CompiledExpression) { + TODO() + } + + actual fun setTextIgnorePlacement(ignorePlacement: CompiledExpression) { + TODO() + } + + actual fun setTextOptional(optional: CompiledExpression) { + TODO() + } + + actual fun setTextOpacity(opacity: CompiledExpression) { + TODO() + } + + actual fun setTextColor(color: CompiledExpression) { + TODO() + } + + actual fun setTextHaloColor(haloColor: CompiledExpression) { + TODO() + } + + actual fun setTextHaloWidth(haloWidth: CompiledExpression) { + TODO() + } + + actual fun setTextHaloBlur(haloBlur: CompiledExpression) { + TODO() + } + + actual fun setTextTranslate(translate: CompiledExpression) { + TODO() + } + + actual fun setTextTranslateAnchor(translateAnchor: CompiledExpression) { + TODO() + } +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/UnknownLayer.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/UnknownLayer.kt new file mode 100644 index 00000000..0e88012b --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/layer/UnknownLayer.kt @@ -0,0 +1,3 @@ +package dev.sargunv.maplibrecompose.core.layer + +internal actual class UnknownLayer(override val impl: Nothing) : Layer() diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt new file mode 100644 index 00000000..b1199e66 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/GeoJsonSource.kt @@ -0,0 +1,20 @@ +package dev.sargunv.maplibrecompose.core.source + +import io.github.dellisd.spatialk.geojson.GeoJson + +public actual class GeoJsonSource : Source { + + @Suppress("UNREACHABLE_CODE") override val impl: Nothing = TODO() + + public actual constructor(id: String, uri: String, options: GeoJsonOptions) + + public actual constructor(id: String, data: GeoJson, options: GeoJsonOptions) + + public actual fun setUri(uri: String) { + TODO() + } + + public actual fun setData(geoJson: GeoJson) { + TODO() + } +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/RasterSource.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/RasterSource.kt new file mode 100644 index 00000000..a6f34a01 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/RasterSource.kt @@ -0,0 +1,6 @@ +package dev.sargunv.maplibrecompose.core.source + +public actual class RasterSource actual constructor(id: String, uri: String, tileSize: Int) : + Source() { + override val impl: Nothing = TODO() +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/Source.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/Source.kt new file mode 100644 index 00000000..df26e8a5 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/Source.kt @@ -0,0 +1,15 @@ +package dev.sargunv.maplibrecompose.core.source + +public actual sealed class Source { + internal abstract val impl: Nothing + + internal actual val id: String + get() = TODO() + + public actual val attributionLinks: List + get() { + TODO() + } + + override fun toString(): String = "${this::class.simpleName}(id=\"$id\")" +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/UnknownSource.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/UnknownSource.kt new file mode 100644 index 00000000..9f64df99 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/UnknownSource.kt @@ -0,0 +1,3 @@ +package dev.sargunv.maplibrecompose.core.source + +public actual class UnknownSource(override val impl: Nothing) : Source() diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/VectorSource.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/VectorSource.kt new file mode 100644 index 00000000..c6c0dd99 --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/source/VectorSource.kt @@ -0,0 +1,5 @@ +package dev.sargunv.maplibrecompose.core.source + +public actual class VectorSource actual constructor(id: String, uri: String) : Source() { + override val impl: Nothing = TODO() +} diff --git a/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/util/PlatformUtils.kt b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/util/PlatformUtils.kt new file mode 100644 index 00000000..78a2fdec --- /dev/null +++ b/lib/maplibre-compose/src/jsMain/kotlin/dev/sargunv/maplibrecompose/core/util/PlatformUtils.kt @@ -0,0 +1,7 @@ +package dev.sargunv.maplibrecompose.core.util + +import androidx.compose.runtime.Composable + +public actual object PlatformUtils { + @Composable public actual fun getSystemRefreshRate(): Float = 0f // not supported +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 7529c3b4..b2d520dc 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -42,4 +42,5 @@ include( ":lib:maplibre-compose-expressions", ":lib:maplibre-compose-webview", ":lib:kotlin-maplibre-js", + ":lib:compose-html-interop", )