diff --git a/gradle.properties b/gradle.properties
index 5d17721..755b00d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -22,3 +22,7 @@ android.enableJetifier=false
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.mpp.androidGradlePluginCompatibility.nowarn=true
org.jetbrains.compose.experimental.uikit.enabled=true
+
+#Compose for Web is Experimental
+org.jetbrains.compose.experimental.jscanvas.enabled=true
+org.jetbrains.compose.experimental.wasm.enabled=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 34ec08b..772aeaf 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -6,7 +6,7 @@ agp = "8.1.4"
androidx-compose-ui = "1.6.0" # https://developer.android.com/jetpack/androidx/releases/compose-ui
androidx-compose-ui-material3 = "1.1.2"
androidx-compose-compiler = "1.5.8" # https://developer.android.com/jetpack/androidx/releases/compose-compiler
-compose-multiplatform = "1.5.12" # https://github.com/JetBrains/compose-multiplatform/releases
+compose-multiplatform = "1.6.0" # https://github.com/JetBrains/compose-multiplatform/releases
androidx-appcompat = "1.6.1"
androidx-activity = "1.8.2" # https://developer.android.com/jetpack/androidx/releases/activity
androidx-savedstate = "1.2.1" # https://developer.android.com/jetpack/androidx/releases/savedstate
diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock
new file mode 100644
index 0000000..e69de29
diff --git a/library/build.gradle.kts b/library/build.gradle.kts
index d2719c8..2c76077 100644
--- a/library/build.gradle.kts
+++ b/library/build.gradle.kts
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
+
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.multiplatform)
@@ -15,6 +17,13 @@ kotlin {
iosX64()
iosArm64()
iosSimulatorArm64()
+ js(IR) {
+ browser()
+ }
+ @OptIn(ExperimentalWasmDsl::class)
+ wasmJs {
+ browser()
+ }
sourceSets {
val commonMain by getting {
diff --git a/sample/.gitignore b/sample/androidApp/.gitignore
similarity index 100%
rename from sample/.gitignore
rename to sample/androidApp/.gitignore
diff --git a/sample/build.gradle.kts b/sample/androidApp/build.gradle.kts
similarity index 96%
rename from sample/build.gradle.kts
rename to sample/androidApp/build.gradle.kts
index dd80ee4..be83674 100644
--- a/sample/build.gradle.kts
+++ b/sample/androidApp/build.gradle.kts
@@ -30,6 +30,7 @@ android {
dependencies {
implementation(projects.library)
+ implementation(projects.sample.shared)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.activity)
implementation(libs.compose.foundation)
diff --git a/sample/src/main/AndroidManifest.xml b/sample/androidApp/src/main/AndroidManifest.xml
similarity index 100%
rename from sample/src/main/AndroidManifest.xml
rename to sample/androidApp/src/main/AndroidManifest.xml
diff --git a/sample/androidApp/src/main/kotlin/me/saket/swipe/sample/SampleActivity.kt b/sample/androidApp/src/main/kotlin/me/saket/swipe/sample/SampleActivity.kt
new file mode 100644
index 0000000..5291411
--- /dev/null
+++ b/sample/androidApp/src/main/kotlin/me/saket/swipe/sample/SampleActivity.kt
@@ -0,0 +1,51 @@
+package me.saket.swipe.sample
+
+import android.os.Build
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.*
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.core.view.WindowCompat
+import com.google.accompanist.systemuicontroller.rememberSystemUiController
+import me.saket.swipe.sample.theme.DarkTheme
+import me.saket.swipe.sample.theme.LightTheme
+import me.saket.swipe.sample.theme.MainContent
+
+@OptIn(ExperimentalMaterial3Api::class)
+class SampleActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ val uiController = rememberSystemUiController()
+ val systemInDarkTheme = isSystemInDarkTheme()
+ LaunchedEffect(Unit) {
+ WindowCompat.setDecorFitsSystemWindows(window, false)
+ uiController.setSystemBarsColor(Color.Transparent, darkIcons = !systemInDarkTheme)
+ uiController.setNavigationBarColor(Color.Transparent)
+ }
+
+ val colors = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ if (systemInDarkTheme) dynamicDarkColorScheme(this) else dynamicLightColorScheme(this)
+ } else {
+ if (systemInDarkTheme) DarkTheme else LightTheme
+ }
+
+ MaterialTheme(colors) {
+ Scaffold(
+ topBar = {
+ TopAppBar(title = { Text(stringResource(R.string.app_name)) })
+ }
+ ) { contentPadding ->
+ MainContent(modifier = Modifier.padding(contentPadding))
+ }
+ }
+ }
+ }
+}
diff --git a/sample/src/main/res/drawable/ic_launcher_background.xml b/sample/androidApp/src/main/res/drawable/ic_launcher_background.xml
similarity index 100%
rename from sample/src/main/res/drawable/ic_launcher_background.xml
rename to sample/androidApp/src/main/res/drawable/ic_launcher_background.xml
diff --git a/sample/src/main/res/drawable/ic_launcher_foreground.xml b/sample/androidApp/src/main/res/drawable/ic_launcher_foreground.xml
similarity index 100%
rename from sample/src/main/res/drawable/ic_launcher_foreground.xml
rename to sample/androidApp/src/main/res/drawable/ic_launcher_foreground.xml
diff --git a/sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/sample/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
similarity index 100%
rename from sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to sample/androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
diff --git a/sample/src/main/res/values/strings.xml b/sample/androidApp/src/main/res/values/strings.xml
similarity index 100%
rename from sample/src/main/res/values/strings.xml
rename to sample/androidApp/src/main/res/values/strings.xml
diff --git a/sample/src/main/res/values/styles.xml b/sample/androidApp/src/main/res/values/styles.xml
similarity index 100%
rename from sample/src/main/res/values/styles.xml
rename to sample/androidApp/src/main/res/values/styles.xml
diff --git a/sample/shared/build.gradle.kts b/sample/shared/build.gradle.kts
new file mode 100644
index 0000000..f7bb493
--- /dev/null
+++ b/sample/shared/build.gradle.kts
@@ -0,0 +1,72 @@
+import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
+
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.multiplatform)
+ alias(libs.plugins.compose.multiplatform)
+}
+
+kotlin {
+ @Suppress("OPT_IN_USAGE")
+ targetHierarchy.default()
+
+ androidTarget()
+ jvm()
+ iosX64()
+ iosArm64()
+ iosSimulatorArm64()
+ js(IR) {
+ browser()
+ }
+ @OptIn(ExperimentalWasmDsl::class)
+ wasmJs {
+ browser()
+ }
+
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(projects.library)
+ implementation(compose.ui)
+ implementation(compose.foundation)
+ implementation(compose.material3)
+ implementation(compose.materialIconsExtended)
+ @OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
+ implementation(compose.components.resources)
+ }
+ }
+
+ val jsWasmMain by creating {
+ dependsOn(commonMain)
+ }
+
+ val jsMain by getting {
+ dependsOn(jsWasmMain)
+ }
+
+ val wasmJsMain by getting {
+ dependsOn(jsWasmMain)
+ }
+ }
+}
+
+android {
+ namespace = "me.saket.swipe"
+
+ defaultConfig {
+ minSdk = libs.versions.minSdk.get().toInt()
+ compileSdk = libs.versions.compileSdk.get().toInt()
+ }
+ buildFeatures {
+ compose = true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion = libs.versions.androidx.compose.compiler.get()
+ }
+ java {
+ toolchain.languageVersion.set(JavaLanguageVersion.of(11))
+ }
+ lint {
+ abortOnError = true
+ }
+}
diff --git a/sample/shared/src/commonMain/composeResources/values/strings.xml b/sample/shared/src/commonMain/composeResources/values/strings.xml
new file mode 100644
index 0000000..ffc7d62
--- /dev/null
+++ b/sample/shared/src/commonMain/composeResources/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Swipe
+
diff --git a/sample/src/main/kotlin/me/saket/swipe/sample/SampleActivity.kt b/sample/shared/src/commonMain/kotlin/me/saket/swipe/sample/theme/MainContent.kt
similarity index 60%
rename from sample/src/main/kotlin/me/saket/swipe/sample/SampleActivity.kt
rename to sample/shared/src/commonMain/kotlin/me/saket/swipe/sample/theme/MainContent.kt
index aad42ec..f16ca8f 100644
--- a/sample/src/main/kotlin/me/saket/swipe/sample/SampleActivity.kt
+++ b/sample/shared/src/commonMain/kotlin/me/saket/swipe/sample/theme/MainContent.kt
@@ -1,19 +1,8 @@
-package me.saket.swipe.sample
+package me.saket.swipe.sample.theme
-import android.os.Build
-import android.os.Bundle
-import androidx.activity.compose.setContent
-import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -21,16 +10,10 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.twotone.Archive
import androidx.compose.material.icons.twotone.ReplyAll
import androidx.compose.material.icons.twotone.Snooze
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBar
-import androidx.compose.material3.dynamicDarkColorScheme
-import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
@@ -39,50 +22,19 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.rememberVectorPainter
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.core.view.WindowCompat
-import com.google.accompanist.systemuicontroller.rememberSystemUiController
import me.saket.swipe.SwipeAction
import me.saket.swipe.SwipeableActionsBox
-import me.saket.swipe.sample.theme.DarkTheme
-import me.saket.swipe.sample.theme.LightTheme
-@OptIn(ExperimentalMaterial3Api::class)
-class SampleActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContent {
- val uiController = rememberSystemUiController()
- val systemInDarkTheme = isSystemInDarkTheme()
- LaunchedEffect(Unit) {
- WindowCompat.setDecorFitsSystemWindows(window, false)
- uiController.setSystemBarsColor(Color.Transparent, darkIcons = !systemInDarkTheme)
- uiController.setNavigationBarColor(Color.Transparent)
- }
-
- val colors = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- if (systemInDarkTheme) dynamicDarkColorScheme(this) else dynamicLightColorScheme(this)
- } else {
- if (systemInDarkTheme) DarkTheme else LightTheme
- }
+@Composable
+fun MainContent(modifier: Modifier = Modifier) {
- MaterialTheme(colors) {
- Scaffold(
- topBar = {
- TopAppBar(title = { Text(stringResource(R.string.app_name)) })
- }
- ) { contentPadding ->
- LazyColumn(Modifier.padding(contentPadding).fillMaxSize()) {
- items(20) { index ->
- SwipeableBoxPreview(
- Modifier.fillMaxWidth()
- )
- }
- }
- }
- }
+ LazyColumn(modifier.fillMaxSize()) {
+ items(20) { index ->
+ SwipeableBoxPreview(
+ Modifier.fillMaxWidth()
+ )
}
}
}
@@ -125,7 +77,7 @@ private fun SwipeableBoxPreview(modifier: Modifier = Modifier) {
}
@Composable
-private fun BatmanIpsumItem(
+fun BatmanIpsumItem(
modifier: Modifier = Modifier,
isSnoozed: Boolean
) {
diff --git a/sample/src/main/kotlin/me/saket/swipe/sample/theme/Theme.kt b/sample/shared/src/commonMain/kotlin/me/saket/swipe/sample/theme/Theme.kt
similarity index 100%
rename from sample/src/main/kotlin/me/saket/swipe/sample/theme/Theme.kt
rename to sample/shared/src/commonMain/kotlin/me/saket/swipe/sample/theme/Theme.kt
diff --git a/sample/webApp/README.md b/sample/webApp/README.md
new file mode 100644
index 0000000..06fba71
--- /dev/null
+++ b/sample/webApp/README.md
@@ -0,0 +1,7 @@
+# Web app
+
+To start the web app, run:
+
+```shell
+./gradlew :sample:webApp:wasmJsBrowserDevelopmentRun
+```
diff --git a/sample/webApp/build.gradle.kts b/sample/webApp/build.gradle.kts
new file mode 100644
index 0000000..fc3d27a
--- /dev/null
+++ b/sample/webApp/build.gradle.kts
@@ -0,0 +1,58 @@
+import org.jetbrains.compose.ExperimentalComposeLibrary
+import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
+import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
+
+plugins {
+ alias(libs.plugins.kotlin.multiplatform)
+ alias(libs.plugins.compose.multiplatform)
+}
+
+
+kotlin {
+ js(IR) {
+ moduleName = "sample"
+ browser {
+ commonWebpackConfig {
+ outputFileName = "sample.js"
+ }
+ }
+ binaries.executable()
+ }
+
+ @OptIn(ExperimentalWasmDsl::class)
+ wasmJs {
+ moduleName = "sample"
+ browser {
+ commonWebpackConfig {
+ outputFileName = "sample.js"
+ }
+ }
+ binaries.executable()
+ }
+
+ sourceSets {
+ val jsWasmMain by creating {
+ dependencies {
+ implementation(projects.library)
+ implementation(projects.sample.shared)
+ implementation(compose.runtime)
+ implementation(compose.ui)
+ implementation(compose.foundation)
+ implementation(compose.material3)
+ @OptIn(ExperimentalComposeLibrary::class)
+ implementation(compose.components.resources)
+ }
+ }
+ val jsMain by getting {
+ dependsOn(jsWasmMain)
+ }
+ val wasmJsMain by getting {
+ dependsOn(jsWasmMain)
+ }
+ }
+}
+
+
+compose.experimental {
+ web.application {}
+}
diff --git a/sample/webApp/src/jsMain/kotlin/me/saket/swipe/sample/main.kt b/sample/webApp/src/jsMain/kotlin/me/saket/swipe/sample/main.kt
new file mode 100644
index 0000000..271b6cb
--- /dev/null
+++ b/sample/webApp/src/jsMain/kotlin/me/saket/swipe/sample/main.kt
@@ -0,0 +1,14 @@
+package me.saket.swipe.sample
+
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.window.CanvasBasedWindow
+import org.jetbrains.skiko.wasm.onWasmReady
+
+@OptIn(ExperimentalComposeUiApi::class)
+fun main() {
+ onWasmReady {
+ CanvasBasedWindow("Sample") {
+ MainContentWeb()
+ }
+ }
+}
diff --git a/sample/webApp/src/jsMain/resources/index.html b/sample/webApp/src/jsMain/resources/index.html
new file mode 100644
index 0000000..3857eee
--- /dev/null
+++ b/sample/webApp/src/jsMain/resources/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Compose App
+
+
+
+
+
+
+
diff --git a/sample/webApp/src/jsWasmMain/kotlin/me/saket/swipe/sample/Common.kt b/sample/webApp/src/jsWasmMain/kotlin/me/saket/swipe/sample/Common.kt
new file mode 100644
index 0000000..785f0b8
--- /dev/null
+++ b/sample/webApp/src/jsWasmMain/kotlin/me/saket/swipe/sample/Common.kt
@@ -0,0 +1,9 @@
+package me.saket.swipe.sample
+
+import androidx.compose.runtime.Composable
+import me.saket.swipe.sample.theme.MainContent
+
+@Composable
+internal fun MainContentWeb() {
+ MainContent()
+}
diff --git a/sample/webApp/src/wasmJsMain/kotlin/me/saket/swipe/sample/Main.wasm.kt b/sample/webApp/src/wasmJsMain/kotlin/me/saket/swipe/sample/Main.wasm.kt
new file mode 100644
index 0000000..f03b6bd
--- /dev/null
+++ b/sample/webApp/src/wasmJsMain/kotlin/me/saket/swipe/sample/Main.wasm.kt
@@ -0,0 +1,13 @@
+package me.saket.swipe.sample
+
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.window.CanvasBasedWindow
+import me.saket.swipe.sample.theme.MainContent
+
+
+@OptIn(ExperimentalComposeUiApi::class)
+fun main() {
+ CanvasBasedWindow(canvasElementId = "ComposeTarget") {
+ MainContent()
+ }
+}
diff --git a/sample/webApp/src/wasmJsMain/resources/index.html b/sample/webApp/src/wasmJsMain/resources/index.html
new file mode 100644
index 0000000..3857eee
--- /dev/null
+++ b/sample/webApp/src/wasmJsMain/resources/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Compose App
+
+
+
+
+
+
+
diff --git a/settings.gradle b/settings.gradle
index cdef391..eedd38f 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -14,7 +14,9 @@ dependencyResolutionManagement {
}
include ':library'
-include ':sample'
+include ':sample:androidApp'
+include ':sample:webApp'
+include ':sample:shared'
rootProject.name = "swipe"
enableFeaturePreview('TYPESAFE_PROJECT_ACCESSORS')