diff --git a/.gitignore b/.gitignore index c2d545ac..60ba4a58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .gradle +.kotlin build local.properties diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6ca9f898 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,58 @@ +# Change log + +## v0.4.0 / 2024-10-24 + +* fix a bug that a dependent project might crash on Android and add the Android target explicitly for all modules +* no longer publish the legacy module +* bump Kotlin to 2.0.10, Compose Multiplatform to 1.7.0, Kobweb to 0.19.2, and our Compose HTML Material to 0.4.0 + +### Common + +* add `Arrangement.spacedBy` +* Move `LoadingState` here from [Compose HTML Material](https://github.com/huanshankeji/compose-html-material) +* add color parameters to the text composables +* rename `InlineText` to `TaglessText` +* add the `alpha` modifiers +* add a `hidden` modifier +* add the `clickable` modifier and replace `onClick` with it +* add the `PaddingValues` type +* add a `BoxWithConstraints` layout composable that's still buggy on JS DOM +* add `flex-basis: 0` to the weight modifiers on JS DOM to make them consistent with the `androidx.compose` behavior +* split the `padding` modifiers into `outerPadding` and `innerPadding` +* add the `VerticalScrollBox` and `HorizontalScrollBox` composables as (better) alternatives to the `*Scroll` modifiers + +### Material 2 + +* revamp `TopAppBarScaffold` to take a bottom bar, a floating action bottom, and a snackbar host, and fix some of its display issues on JS DOM + * fix bugs that the action buttons don't show and their `onClick` callbacks are not passed on JS DOM +* add the `SnackbarHost` (the Material 3 snackbar is not available in Material Web yet) +* add `RadioGroupColumn` and improve `RadioRow` on JS DOM +* add a platform-specific implementation for `com.huanshankeji.compose.material2.ext.IconButton` on JS DOM that's more idiomatic, and fix a bug that in some scenarios icons are not shown, by always importing "material-icons/iconfont/material-icons.css" + +### Material 3 + +* add the menu composables `DropdownMenu`, `DropdownMenuItem`, `ExposedDropdownMenuBox`, and `ExposedDropdownMenuWithTextField` +* add the progress indicator composables `LinearProgressIndicator` and `CircularProgressIndicator` +* fix a bug in the text fields on JS DOM that causes the caret to be reset to the start whenever the value changes if the `type` attribute is set +* make multiline text fields work on JS DOM +* add an `isInteractiveJsDom` parameter to ListItemComponents + +### Navigation + +* initially support navigation + +### ViewModel + +* initially support ViewModel which delegates to raw UI state on Compose HTML / JS DOM + +### Demo + +* make the demo UI friendly on mobile + +## v0.3.0 / 2024-5-10 + +Support Material 3. See the Updated README.md for more details. + +## v0.2.0 / 2024-4-17 + +The project now depends on Kobweb Silk on Kotlin/JS (Compose HTML) and there is a universal multiplatform interface for `Modifier`, scopes, etc. akin to those in `androidx.compose`. Obsolete code including `ModifierOrAttrsScope` is moved to a legacy module. diff --git a/README.md b/README.md index 611153e1..50de60de 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Compose Multiplatform Material: unified Compose Multiplatform common extensions and Material wrappers for `androidx.compose` and Compose HTML +# Compose Multiplatform Material (better name pending): unified Compose Multiplatform common extensions and Material wrappers for `androidx.compose` and Compose HTML [![Maven Central](https://img.shields.io/maven-central/v/com.huanshankeji/compose-multiplatform-material3)](https://search.maven.org/search?q=g:com.huanshankeji%20AND%20a:compose-multiplatform-*) ![Kotlin version](https://kotlin-version.aws.icerock.dev/kotlin-version?group=com.huanshankeji&name=compose-multiplatform-material3) @@ -21,19 +21,23 @@ This project is still in development and has not reached the stable state yet. S ##### `ext` components -- `InlineBasicText` +- `TaglessBasicText` ##### Layouts -- `Box` -- `Column` (via flexbox on JS) -- `Row` (via flexbox on JS) +- `Box` (based on Kobweb) +- `Column` (via flexbox on JS, based on Kobweb) +- `Row` (via flexbox on JS, based on Kobweb) - `Spacer` +###### `ext` layouts + + - `BoxWithConstraints` + ##### Lazy -- `LazyColumn` -- `LazyRow` +- `LazyColumn` (via flexbox on JS, based on Kobweb) +- `LazyRow` (via flexbox on JS, based on Kobweb) #### Material 2 components @@ -43,6 +47,7 @@ This project is still in development and has not reached the stable state yet. S - `Divider` (not working properly on JS yet) - `Icon` - `IconButton` +- `Snackbar` (inconsistent, not recommended), `SnackBarHost` (recommended) - `Switch` - `Text` @@ -52,7 +57,7 @@ This project is still in development and has not reached the stable state yet. S - `IconButton` - `RadioRow`, `RadioGroupRow` - `SwitchWithLabel` -- `MaterialText`, `InlineText` +- `MaterialText`, `TaglessText` - `TextField`, `OutlinedTextField` - `TopAppBarScaffold` @@ -68,6 +73,7 @@ This project is still in development and has not reached the stable state yet. S - `FloatingActionButton`, `SmallFloatingActionButton`, `LargeFloatingActionButton`, `ExtendedFloatingActionButton` - `Icon` - `IconButton`, `IconToggleButton`, `FilledIconButton`, `FilledIconToggleButton`, `FilledTonalIconButton`, `FilledTonalIconToggleButton`, `OutlinedIconButton`, `OutlinedIconToggleButton` +- `LinearProgressIndicator`, `CircularProgressIndicator` - `Switch` - `Text` @@ -75,10 +81,12 @@ This project is still in development and has not reached the stable state yet. S - `Button` (`FilledButton`), `ElevatedButton`, `FilledTonalButton`, `OutlinedButton`, `TextButton` - `Card` (`FilledCard`), `ElevatedCard`, `OutlinedCard` +- `DropdownMenu`, `DropdownMenuItem` + - `ExposedDropdownMenuBox`, `ExposedDropdownMenuBoxScope.ExposedDropdownMenuBoxTextField`, `ExposedDropdownMenuBoxScope.ExposedDropdownMenu`, `ExposedDropdownMenuWithTextField` - `FloatingActionButton`, `SmallFloatingActionButton`, `LargeFloatingActionButton`, `ExtendedFloatingActionButton` - `IconButton`, `IconToggleButton`, `FilledIconButton`, `FilledIconToggleButton`, `FilledTonalIconButton`, `FilledTonalIconToggleButton`, `OutlinedIconButton`, `OutlinedIconToggleButton` - `NavigationBar`, `NavigationBarItem` -- `MaterialText`, `InlineText` +- `MaterialText`, `TaglessText` - `TextField`, `OutlinedTextField` ##### `lazy.ext` components @@ -89,8 +97,18 @@ This project is still in development and has not reached the stable state yet. S The components in the `ext` packages don't follow the `androidx.compose` APIs exactly, but rather provide wrappers are more idiomatic and conventional on both kinds of targets, wrapping different APIs which can't be unified following the `androidx.compose` APIs. +#### About parameter names + +The parameter names with suffixes such as "JsDom" or "AndroidxCommon" are platform-specific, and only apply on their respective platform(s), Compose HTML / JS DOM or +`androidx.compose` platforms. + +#### Material Icons + +The `com.huanshankeji.compose.material.icons.Icon` class delegates to both kinds of targets, but only a few Material Icons are added as PoC. You need to add your concrete icon implementations following the style of the existing ones in `com.huanshankeji.compose.material.icons` to use the icons you need. Track the progress of full icon support in [#4](/../../issues/4). + ### Modifiers +- `alpha` - size modifiers - `size`, `sizeIn`, `fillMaxSize` - `width`, `widthIn`, `fillMaxWidth` @@ -106,6 +124,26 @@ The components in the `ext` packages don't follow the `androidx.compose` APIs ex - `outerBorder` - `roundedCornerBackgroundAndOuterBorder` +### Other APIs + +- `Alignment` +- `Arrangement` +- `KeyboardOptions` +- `KeyboardActions` +- `PaddingValues` + +### ViewModel + +The ViewModel module currently supports a small subset of the Compose ViewModel APIs, and delegates to raw UI state on +Compose HTML / JS DOM. These APIs are highly experimental now. + +### Navigation + +The navigation module currently supports a small subset of the Compose Navigation APIs, which does not support +transition or animation on Compose HTML / JS DOM. These APIs are also highly experimental now. +See [CMP-4966](https://youtrack.jetbrains.com/issue/CMP-4966) for a bug to avoid. Also, ViewModel-related functions +are not implemented yet on Compose HTML / JS DOM. + ## Add the libraries to your dependency Maven coordinate: @@ -114,6 +152,14 @@ Maven coordinate: "com.huanshankeji:compose-multiplatform-$module:$version" ``` +More specifically: +```kotlin +"com.huanshankeji:compose-multiplatform-common:$version" +"com.huanshankeji:compose-multiplatform-material-icons-core:$version" +"com.huanshankeji:compose-multiplatform-material2:$version" +"com.huanshankeji:compose-multiplatform-material3:$version" +``` + For example, depend on the Material 3 module with Gradle: ```kotlin diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 4536c58c..5af5195f 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -3,16 +3,19 @@ plugins { } repositories { - //mavenLocal() + mavenLocal() // TODO comment out when not needed gradlePluginPortal() google() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") mavenCentral() } dependencies { - implementation(kotlin("gradle-plugin", "1.9.23")) - implementation("org.jetbrains.compose:compose-gradle-plugin:1.6.2") - implementation("com.huanshankeji.team:gradle-plugins:0.5.1") - implementation("com.android.tools.build:gradle:8.2.2") + // With Kotlin 2.0.20, a "Could not parse POM" build error occurs in the JVM projects of some dependent projects. + val kotlinVersion = "2.0.10" + implementation(kotlin("gradle-plugin", kotlinVersion)) + implementation("org.jetbrains.kotlin:compose-compiler-gradle-plugin:$kotlinVersion") + implementation("org.jetbrains.compose:compose-gradle-plugin:1.7.0") + implementation("com.huanshankeji.team:gradle-plugins:0.6.0") // don't use a snapshot version in a main branch + implementation("com.android.tools.build:gradle:8.5.2") + implementation("com.huanshankeji:common-gradle-dependencies:0.8.0-20241016") // don't use a snapshot version in a main branch } diff --git a/buildSrc/src/main/kotlin/Android.kt b/buildSrc/src/main/kotlin/Android.kt new file mode 100644 index 00000000..9d79de1d --- /dev/null +++ b/buildSrc/src/main/kotlin/Android.kt @@ -0,0 +1 @@ +val androidSdkVersion = 34 // bump to 35 when AGP and Kotlin are bumped diff --git a/buildSrc/src/main/kotlin/Constants.kt b/buildSrc/src/main/kotlin/Constants.kt new file mode 100644 index 00000000..cfcdfdb0 --- /dev/null +++ b/buildSrc/src/main/kotlin/Constants.kt @@ -0,0 +1,3 @@ +const val FOR_COMPOSE_TARGETS_IN_TITLE = "for `androidx.compose` and Compose HTML" +const val FOR_COMPOSE_TARGETS_IN_DESCRIPTION = + "for `androidx.compose` (Android, desktop (JVM), iOS, and web (Kotlin/Wasm)) and Compose HTML" diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 54ab91fa..86221b19 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -1,16 +1,14 @@ +import com.huanshankeji.CommonDependencies import org.jetbrains.compose.ComposeBuildConfig -val projectVersion = "0.3.0" +val projectVersion = "0.4.0" + +val commonDependencies = CommonDependencies() object DependencyVersions { const val composeMultiplatform = ComposeBuildConfig.composeVersion // for "ui-unit" - val kobweb = "0.17.3" - val huanshankejiComposeHtml = "0.3.0" + val kobweb = "0.19.2" + val huanshankejiComposeHtml = "0.4.0" // don't use a snapshot version in a main branch val kmdc = "0.1.2" - val materialSymbols = "0.17.4" - - object Androidx { - val activityCompose = "1.9.0" - val compose = "1.6.6" - } + val materialSymbols = "0.25.1" } diff --git a/buildSrc/src/main/kotlin/common-conventions.gradle.kts b/buildSrc/src/main/kotlin/common-conventions.gradle.kts index 33fca5d7..e525ae45 100644 --- a/buildSrc/src/main/kotlin/common-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/common-conventions.gradle.kts @@ -3,6 +3,7 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl plugins { kotlin("multiplatform") + kotlin("plugin.compose") id("org.jetbrains.compose") } @@ -10,7 +11,6 @@ repositories { mavenLocal() mavenCentral() google() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") maven("https://us-central1-maven.pkg.dev/varabyte-repos/public") // for Kobweb } diff --git a/buildSrc/src/main/kotlin/lib-conventions-without-publishing.gradle.kts b/buildSrc/src/main/kotlin/lib-conventions-without-publishing.gradle.kts new file mode 100644 index 00000000..c05e8410 --- /dev/null +++ b/buildSrc/src/main/kotlin/lib-conventions-without-publishing.gradle.kts @@ -0,0 +1,42 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi + +plugins { + id("common-conventions") + id("com.huanshankeji.kotlin-multiplatform-conventional-targets") + id("com.android.library") +} + +kotlin { + androidTarget { + publishLibraryVariants("release", "debug") + } + + // move to `common-conventions` if necessary + + @OptIn(ExperimentalKotlinGradlePluginApi::class) + applyDefaultHierarchyTemplate { + common { + group("androidxCommon") { + withJvm() + withAndroidTarget() + group("ios") + withWasmJs() + } + } + } + + /* + sourceSets { + val androidxCommonMain by creating { dependsOn(commonMain.get()) } + jvmMain { dependsOn(androidxCommonMain) } + iosMain { dependsOn(androidxCommonMain) } + named("wasmJsMain") { dependsOn(androidxCommonMain) } + } + */ +} + +android { + namespace = group as String + + compileSdk = androidSdkVersion +} diff --git a/buildSrc/src/main/kotlin/lib-conventions.gradle.kts b/buildSrc/src/main/kotlin/lib-conventions.gradle.kts index dd405fc0..e96eb9ed 100644 --- a/buildSrc/src/main/kotlin/lib-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/lib-conventions.gradle.kts @@ -1,33 +1,4 @@ -import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi - plugins { - id("common-conventions") - // TODO: `id("com.android.library") version "7.2.2"`? - id("com.huanshankeji.kotlin-multiplatform-jvm-and-js-browser-conventions") + id("lib-conventions-without-publishing") id("com.huanshankeji.kotlin-multiplatform-sonatype-ossrh-publish-conventions") - //id("com.android.library") // not used yet, the `jvm` target seems to be enough -} - -kotlin { - // move to `common-conventions` if necessary - - @OptIn(ExperimentalKotlinGradlePluginApi::class) - applyDefaultHierarchyTemplate { - common { - group("androidxCommon") { - withJvm() - group("ios") - withWasm() - } - } - } - - /* - sourceSets { - val androidxCommonMain by creating { dependsOn(commonMain.get()) } - jvmMain { dependsOn(androidxCommonMain) } - iosMain { dependsOn(androidxCommonMain) } - named("wasmJsMain") { dependsOn(androidxCommonMain) } - } - */ } \ No newline at end of file diff --git a/compose-multiplatform-common/build.gradle.kts b/compose-multiplatform-common/build.gradle.kts index 1c94940d..91153060 100644 --- a/compose-multiplatform-common/build.gradle.kts +++ b/compose-multiplatform-common/build.gradle.kts @@ -1,11 +1,27 @@ import com.huanshankeji.team.`Shreck Ye` import com.huanshankeji.team.pomForTeamDefaultOpenSource +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi plugins { `lib-conventions` } kotlin { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + applyDefaultHierarchyTemplate { + common { + group("androidxCommon") { + group("androidxCommonExceptAndroid") { + withJvm() + group("ios") + withWasmJs() + } + } + withAndroidTarget() + } + } + + sourceSets { /* Use `api`. See: @@ -31,6 +47,7 @@ kotlin { androidxCommonMain { dependencies { api(compose.foundation) + implementation(compose.ui) } } jsMain { @@ -47,8 +64,8 @@ kotlin { publishing.publications.withType { pomForTeamDefaultOpenSource( project, - "Compose Multiplatform common wrappers", - "Common wrappers of components (including layouts) and styles for Compose Multiplatform on (desktop/Android and web)" + "Unified Compose Multiplatform common wrappers $FOR_COMPOSE_TARGETS_IN_TITLE", + "Common wrappers of components (including layouts) and modifiers $FOR_COMPOSE_TARGETS_IN_DESCRIPTION" ) { `Shreck Ye`() } diff --git a/compose-multiplatform-common/legacy/build.gradle.kts b/compose-multiplatform-common/legacy/build.gradle.kts index 8f0df908..88844067 100644 --- a/compose-multiplatform-common/legacy/build.gradle.kts +++ b/compose-multiplatform-common/legacy/build.gradle.kts @@ -1,8 +1,5 @@ -import com.huanshankeji.team.`Shreck Ye` -import com.huanshankeji.team.pomForTeamDefaultOpenSource - plugins { - `lib-conventions` + `lib-conventions-without-publishing` } kotlin { @@ -28,6 +25,7 @@ kotlin { } } +/* publishing.publications.withType { pomForTeamDefaultOpenSource( project, @@ -39,3 +37,4 @@ publishing.publications.withType { `Shreck Ye`() } } +*/ diff --git a/compose-multiplatform-common/src/androidMain/kotlin/com/huanshankeji/compose/foundation/OnClick.android.kt b/compose-multiplatform-common/src/androidMain/kotlin/com/huanshankeji/compose/foundation/OnClick.android.kt new file mode 100644 index 00000000..34bc2bc9 --- /dev/null +++ b/compose-multiplatform-common/src/androidMain/kotlin/com/huanshankeji/compose/foundation/OnClick.android.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.foundation + +import androidx.compose.foundation.clickable +import com.huanshankeji.compose.ui.Modifier + +@ExperimentalFoundationApi +actual fun Modifier.onClick(onClick: () -> Unit): Modifier = + platformModify { clickable(onClick = onClick) } diff --git a/compose-multiplatform-common/src/androidxCommonExceptAndroidMain/kotlin/com/huanshankeji/compose/foundation/OnClick.androidxCommonExceptAndroid.kt b/compose-multiplatform-common/src/androidxCommonExceptAndroidMain/kotlin/com/huanshankeji/compose/foundation/OnClick.androidxCommonExceptAndroid.kt new file mode 100644 index 00000000..c9585b03 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonExceptAndroidMain/kotlin/com/huanshankeji/compose/foundation/OnClick.androidxCommonExceptAndroid.kt @@ -0,0 +1,10 @@ +package com.huanshankeji.compose.foundation + +import androidx.compose.foundation.onClick +import com.huanshankeji.compose.ui.Modifier + +//@ExperimentalFoundationApi // `onClick` with a single parameter is likely to be always supported, so it does not require opt-in for now. +@ExperimentalFoundationApi +@OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class) +actual fun Modifier.onClick(onClick: () -> Unit): Modifier = + platformModify { onClick(onClick = onClick) } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/OnClick.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/OnClick.androidxCommon.kt index 0ccd6995..3957cdc3 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/OnClick.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/OnClick.androidxCommon.kt @@ -1,9 +1,8 @@ package com.huanshankeji.compose.foundation -import androidx.compose.foundation.onClick +import androidx.compose.foundation.clickable import com.huanshankeji.compose.ui.Modifier -//@ExperimentalFoundationApi // `onClick` with a single parameter is likely to be always supported, so it does not require opt-in for now. -@OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class) -actual fun Modifier.onClick(onClick: () -> Unit): Modifier = - platformModify { onClick(onClick = onClick) } +@ExperimentalFoundationApi +actual fun Modifier.clickable(onClick: () -> Unit): Modifier = + platformModify { clickable(onClick = onClick) } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.androidxCommon.kt index 068fe97e..5fb67e58 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.androidxCommon.kt @@ -4,6 +4,9 @@ import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.Box +import com.huanshankeji.compose.foundation.layout.BoxScope +import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier @Composable @@ -17,3 +20,21 @@ actual fun Modifier.verticalScroll(state: ScrollState): Modifier = actual fun Modifier.horizontalScroll(state: ScrollState): Modifier = platformModify { horizontalScroll(state) } + +@Composable +actual fun VerticalScrollBox( + boxModifier: Modifier, + contentModifier: Modifier, + contentAlignment: Alignment, + content: @Composable BoxScope.() -> Unit +) = + Box(boxModifier.verticalScroll(rememberScrollState()).then(contentModifier), contentAlignment, content) + +@Composable +actual fun HorizontalScrollBox( + boxModifier: Modifier, + contentModifier: Modifier, + contentAlignment: Alignment, + content: @Composable BoxScope.() -> Unit +) = + Box(boxModifier.horizontalScroll(rememberScrollState()).then(contentModifier), contentAlignment, content) diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/ext/Border.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/ext/Border.androidxCommon.kt index 8c78fecb..1642f43b 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/ext/Border.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/ext/Border.androidxCommon.kt @@ -7,8 +7,8 @@ import androidx.compose.runtime.Stable import androidx.compose.ui.unit.Dp import com.huanshankeji.compose.foundation.BorderStroke import com.huanshankeji.compose.foundation.border +import com.huanshankeji.compose.foundation.layout.padding import com.huanshankeji.compose.foundation.toPlatformValue -import com.huanshankeji.compose.layout.padding import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.graphics.Color diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/ext/MatchPositionRelativeParentJsOnly.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/ext/MatchPositionRelativeParentJsOnly.androidxCommon.kt new file mode 100644 index 00000000..61640417 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/ext/MatchPositionRelativeParentJsOnly.androidxCommon.kt @@ -0,0 +1,6 @@ +package com.huanshankeji.compose.foundation.ext + +import com.huanshankeji.compose.ui.Modifier + +actual fun Modifier.matchPositionRelativeParentJsDom(): Modifier = + this diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.androidxCommon.kt index 1a19c0f1..74a10876 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.androidxCommon.kt @@ -2,6 +2,7 @@ package com.huanshankeji.compose.foundation.layout import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp import androidx.compose.foundation.layout.Arrangement as PlatformArrangement @Immutable @@ -50,4 +51,8 @@ actual object Arrangement { @Stable actual val SpaceAround: HorizontalOrVertical = HorizontalOrVertical.Impl(PlatformArrangement.SpaceAround) + + @Stable + actual fun spacedBy(space: Dp): HorizontalOrVertical = + HorizontalOrVertical.Impl(PlatformArrangement.spacedBy(space)) } \ No newline at end of file diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Intrinsic.androidxCommon.kt similarity index 93% rename from compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.androidxCommon.kt rename to compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Intrinsic.androidxCommon.kt index 836383f0..62dd76e1 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Intrinsic.androidxCommon.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.layout +package com.huanshankeji.compose.foundation.layout import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Padding.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Padding.androidxCommon.kt new file mode 100644 index 00000000..92a2166b --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Padding.androidxCommon.kt @@ -0,0 +1,70 @@ +package com.huanshankeji.compose.foundation.layout + +import androidx.compose.foundation.layout.absolutePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import com.huanshankeji.compose.ui.Modifier +import androidx.compose.foundation.layout.PaddingValues as PlatformPaddingValues + +@Stable +actual fun Modifier.padding(start: Dp, top: Dp, end: Dp, bottom: Dp): Modifier = + platformModify { padding(start, top, end, bottom) } + +@Stable +actual fun Modifier.padding(horizontal: Dp, vertical: Dp): Modifier = + platformModify { padding(horizontal, vertical) } + +@Stable +actual fun Modifier.padding(all: Dp): Modifier = + platformModify { padding(all) } + +@Stable +actual fun Modifier.padding(paddingValues: PaddingValues): Modifier = + platformModify { padding(paddingValues.platformValue) } + +@Stable +actual fun Modifier.absolutePadding(left: Dp, top: Dp, right: Dp, bottom: Dp): Modifier = + platformModify { absolutePadding(left, top, right, bottom) } + +//actual typealias PaddingValues = androidx.compose.foundation.layout.PaddingValues +@Stable +actual abstract class PaddingValues(val platformValue: PlatformPaddingValues) { + @Immutable + actual class Absolute(platformValue: PlatformPaddingValues.Absolute) : PaddingValues(platformValue) { + actual constructor( + left: Dp, + top: Dp, + right: Dp, + bottom: Dp + ) : this(PlatformPaddingValues.Absolute(left, top, right, bottom)) + } +} + +@Stable +actual fun PaddingValues(all: Dp): PaddingValues = + PaddingValuesImpl(PlatformPaddingValues(all)) + +@Stable +actual fun PaddingValues(horizontal: Dp, vertical: Dp): PaddingValues = + PaddingValuesImpl(PlatformPaddingValues(horizontal, vertical)) + +@Stable +actual fun PaddingValues(start: Dp, top: Dp, end: Dp, bottom: Dp): PaddingValues = + PaddingValuesImpl(PlatformPaddingValues(start, top, end, bottom)) + +@Immutable +internal class PaddingValuesImpl( + platformValue: PlatformPaddingValues +) : PaddingValues(platformValue) + +fun PlatformPaddingValues.toCommonValue(): PaddingValues = + /* + // This seems not necessary. + when (this) { + is PlatformPaddingValues.Absolute -> PaddingValues.Absolute(this) + else -> PaddingValuesImpl(this) + } + */ + PaddingValuesImpl(this) diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.androidxCommon.kt index 56babac8..96f23933 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.androidxCommon.kt @@ -31,6 +31,7 @@ actual interface RowScope { @JvmInline actual value class Impl(override val platformValue: PlatformRowScope) : RowScope + @Stable actual fun Modifier.weight( @FloatRange(from = 0.0, fromInclusive = false) weight: Float diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/Size.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Size.androidxCommon.kt similarity index 97% rename from compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/Size.androidxCommon.kt rename to compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Size.androidxCommon.kt index 58298a04..7042dfcb 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/Size.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Size.androidxCommon.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.layout +package com.huanshankeji.compose.foundation.layout import androidx.annotation.FloatRange import androidx.compose.foundation.layout.* diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/BoxWithConstraints.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/BoxWithConstraints.androidxCommon.kt new file mode 100644 index 00000000..7ce3da0c --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/BoxWithConstraints.androidxCommon.kt @@ -0,0 +1,29 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import com.huanshankeji.compose.foundation.ExperimentalFoundationApi +import com.huanshankeji.compose.ui.Alignment +import com.huanshankeji.compose.ui.Modifier + +@ExperimentalFoundationApi +@Composable +actual fun BoxWithConstraints( + modifier: Modifier, + contentAlignment: Alignment, + content: @Composable BoxWithConstraintsScope.() -> Unit +) = + androidx.compose.foundation.layout.BoxWithConstraints( + modifier.platformModifier, contentAlignment.platformAlignment/*, false*/ + ) { + BoxWithConstraintsScopeImpl(this, maxWidth, maxHeight).content() + } + +@Stable +class BoxWithConstraintsScopeImpl( + override val platformBoxScope: BoxScope, + override val maxWidth: Dp, + override val maxHeight: Dp +) : BoxWithConstraintsScope diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/InnerPadding.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/InnerPadding.androidxCommon.kt new file mode 100644 index 00000000..b7424fd6 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/InnerPadding.androidxCommon.kt @@ -0,0 +1,28 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import com.huanshankeji.compose.foundation.layout.PaddingValues +import com.huanshankeji.compose.foundation.layout.absolutePadding +import com.huanshankeji.compose.foundation.layout.padding +import com.huanshankeji.compose.ui.Modifier + +@Stable +actual fun Modifier.innerPadding(start: Dp, top: Dp, end: Dp, bottom: Dp): Modifier = + padding(start, top, end, bottom) + +@Stable +actual fun Modifier.innerPadding(horizontal: Dp, vertical: Dp): Modifier = + padding(horizontal, vertical) + +@Stable +actual fun Modifier.innerPadding(all: Dp): Modifier = + padding(all) + +@Stable +actual fun Modifier.innerPadding(paddingValues: PaddingValues): Modifier = + padding(paddingValues) + +@Stable +actual fun Modifier.absoluteInnerPadding(left: Dp, top: Dp, right: Dp, bottom: Dp): Modifier = + absolutePadding(left, top, right, bottom) diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/OuterPadding.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/OuterPadding.androidxCommon.kt new file mode 100644 index 00000000..1541c659 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/OuterPadding.androidxCommon.kt @@ -0,0 +1,28 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import com.huanshankeji.compose.foundation.layout.PaddingValues +import com.huanshankeji.compose.foundation.layout.absolutePadding +import com.huanshankeji.compose.foundation.layout.padding +import com.huanshankeji.compose.ui.Modifier + +@Stable +actual fun Modifier.outerPadding(start: Dp, top: Dp, end: Dp, bottom: Dp): Modifier = + padding(start, top, end, bottom) + +@Stable +actual fun Modifier.outerPadding(horizontal: Dp, vertical: Dp): Modifier = + padding(horizontal, vertical) + +@Stable +actual fun Modifier.outerPadding(all: Dp): Modifier = + padding(all) + +@Stable +actual fun Modifier.outerPadding(paddingValues: PaddingValues): Modifier = + padding(paddingValues) + +@Stable +actual fun Modifier.absoluteOuterPadding(left: Dp, top: Dp, right: Dp, bottom: Dp): Modifier = + absolutePadding(left, top, right, bottom) diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Size.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Size.androidxCommon.kt new file mode 100644 index 00000000..345fa4e7 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Size.androidxCommon.kt @@ -0,0 +1,19 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier + +@Stable +actual fun Modifier.fillMaxWidthStretch(): Modifier = + platformModify { fillMaxWidth() } + +@Stable +actual fun Modifier.fillMaxHeightStretch(): Modifier = + platformModify { fillMaxHeight() } + +@Stable +actual fun Modifier.fillMaxSizeStretch(): Modifier = + platformModify { fillMaxSize() } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.androidxCommon.kt index 0047c1ba..07c9605c 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.androidxCommon.kt @@ -2,9 +2,12 @@ package com.huanshankeji.compose.foundation.lazy import androidx.compose.foundation.lazy.LazyScopeMarker import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp import com.huanshankeji.compose.foundation.layout.Arrangement +import com.huanshankeji.compose.foundation.layout.PaddingValues import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier +import androidx.compose.foundation.layout.PaddingValues as PlatformPaddingValues @LazyScopeMarker //@JvmDefaultWithCompatibility @@ -29,6 +32,7 @@ actual class LazyListScope(val platformValue: androidx.compose.foundation.lazy.L @Composable actual fun LazyRow( modifier: Modifier, + contentPadding: PaddingValues?, reverseLayout: Boolean, horizontalArrangement: Arrangement.Horizontal, verticalAlignment: Alignment.Vertical, @@ -36,6 +40,7 @@ actual fun LazyRow( ) = androidx.compose.foundation.lazy.LazyRow( modifier.platformModifier, + contentPadding = contentPadding?.platformValue ?: PlatformPaddingValues(0.dp), reverseLayout = reverseLayout, horizontalArrangement = horizontalArrangement.platformValue, verticalAlignment = verticalAlignment.platformHorizontal @@ -44,6 +49,7 @@ actual fun LazyRow( @Composable actual fun LazyColumn( modifier: Modifier, + contentPadding: PaddingValues?, reverseLayout: Boolean, verticalArrangement: Arrangement.Vertical, horizontalAlignment: Alignment.Horizontal, @@ -51,6 +57,7 @@ actual fun LazyColumn( ) = androidx.compose.foundation.lazy.LazyColumn( modifier.platformModifier, + contentPadding = contentPadding?.platformValue ?: PlatformPaddingValues(0.dp), reverseLayout = reverseLayout, verticalArrangement = verticalArrangement.platformValue, horizontalAlignment = horizontalAlignment.platformHorizontal diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.androidxCommon.kt index 9c1e775a..21355c6e 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.androidxCommon.kt @@ -2,12 +2,10 @@ package com.huanshankeji.compose.foundation.text import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.ColorProducer +import com.huanshankeji.compose.ui.graphics.toNullablePlatformValue import androidx.compose.foundation.text.BasicText as PlatformBasicText @Composable -actual fun BasicText(text: String) = - PlatformBasicText(text) - -@Composable -actual fun BasicText(text: String, modifier: Modifier) = - PlatformBasicText(text, modifier.platformModifier) +actual fun BasicText(text: String, modifier: Modifier, color: ColorProducer?) = + PlatformBasicText(text, modifier.platformModifier, color = color.toNullablePlatformValue()) diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.androidxCommon.kt index 937a9405..5307010c 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.androidxCommon.kt @@ -4,5 +4,5 @@ import androidx.compose.runtime.Composable import com.huanshankeji.compose.foundation.text.BasicText @Composable -actual fun InlineBasicText(text: String) = +actual fun TaglessBasicText(text: String) = BasicText(text) diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/Padding.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/Padding.androidxCommon.kt deleted file mode 100644 index 51bf2bdc..00000000 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/Padding.androidxCommon.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.huanshankeji.compose.layout - -import androidx.compose.foundation.layout.absolutePadding -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Stable -import androidx.compose.ui.unit.Dp -import com.huanshankeji.compose.ui.Modifier - -@Stable -actual fun Modifier.padding(start: Dp, top: Dp, end: Dp, bottom: Dp): Modifier = - platformModify { padding(start, top, end, bottom) } - -@Stable -actual fun Modifier.padding(horizontal: Dp, vertical: Dp): Modifier = - platformModify { padding(horizontal, vertical) } - -@Stable -actual fun Modifier.padding(all: Dp): Modifier = - platformModify { padding(all) } - -@Stable -actual fun Modifier.absolutePadding(left: Dp, top: Dp, right: Dp, bottom: Dp): Modifier = - platformModify { absolutePadding(left, top, right, bottom) } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/ext/Hidden.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/ext/Hidden.androidxCommon.kt new file mode 100644 index 00000000..7340bc74 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/ext/Hidden.androidxCommon.kt @@ -0,0 +1,14 @@ +package com.huanshankeji.compose.layout.ext + +import androidx.compose.ui.layout.layout +import com.huanshankeji.compose.ui.Modifier + +// This approach comes from Gemini: https://g.co/gemini/share/27cfdc47ecff. +// also see: https://developer.android.com/develop/ui/compose/layouts/custom +actual fun Modifier.hidden(): Modifier = + platformModify { + layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + layout(placeable.width, placeable.height) {} + } + } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/draw/Alpha.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/draw/Alpha.androidxCommon.kt new file mode 100644 index 00000000..a757ee10 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/draw/Alpha.androidxCommon.kt @@ -0,0 +1,7 @@ +package com.huanshankeji.compose.ui.draw + +import androidx.compose.ui.draw.alpha +import com.huanshankeji.compose.ui.Modifier + +actual fun Modifier.alpha(alpha: Float): Modifier = + platformModify { alpha(alpha) } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/draw/Rotate.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/draw/Rotate.androidxCommon.kt new file mode 100644 index 00000000..0a9fa499 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/draw/Rotate.androidxCommon.kt @@ -0,0 +1,9 @@ +package com.huanshankeji.compose.ui.draw + +import androidx.compose.runtime.Stable +import androidx.compose.ui.draw.rotate +import com.huanshankeji.compose.ui.Modifier + +@Stable +actual fun Modifier.rotate(degrees: Float): Modifier = + platformModify { rotate(degrees) } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.androidxCommon.kt index af7c6f1a..2c8cce2a 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.androidxCommon.kt @@ -94,3 +94,18 @@ actual fun Color.luminance(): Float = @Stable actual fun Color.toArgb(): Int = platformValue.toArgb() + + +/** + * Converts a nullable [Color] to [PlatformColor]. + */ +fun Color?.toPlatformValue() = + this?.platformValue ?: PlatformColor.Unspecified + +fun ColorProducer.toPlatformValue() = + androidx.compose.ui.graphics.ColorProducer { + this().platformValue + } + +fun ColorProducer?.toNullablePlatformValue() = + this?.toPlatformValue() diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ext/LoadingState.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ext/LoadingState.kt new file mode 100644 index 00000000..a82f993c --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ext/LoadingState.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.ext + +import androidx.compose.runtime.Immutable + +@Immutable +sealed class LoadingState { + object Loading : LoadingState() + class Loaded(val value: T) : LoadingState() + class Error(val error: E) : LoadingState() +} + +typealias ThrowableErrorLoadingState = LoadingState +typealias StringErrorLoadingState = LoadingState diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/Background.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/Background.kt index 6ae59034..b6befc20 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/Background.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/Background.kt @@ -1,6 +1,13 @@ package com.huanshankeji.compose.foundation +import com.huanshankeji.compose.foundation.layout.Box +import com.huanshankeji.compose.foundation.layout.ext.outerPadding import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.graphics.Color +/** + * For consistency on different platforms, if used with a padding modifier, + * use with [Modifier.outerPadding] and use after it, because the padding is not within the background. + * Otherwise, add an outer [Box] and use [background] on it. + */ expect fun Modifier.background(color: Color): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/OnClick.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/OnClick.kt index e9415572..68f88dce 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/OnClick.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/OnClick.kt @@ -2,4 +2,15 @@ package com.huanshankeji.compose.foundation import com.huanshankeji.compose.ui.Modifier +/** + * Delegates to `clickable` on Android. `onClick` is not available on Android. See https://github.com/JetBrains/compose-multiplatform/issues/4468 for details. + */ +@Deprecated("Use `clickable` instead.", ReplaceWith("clickable(onClick)")) +@ExperimentalFoundationApi expect fun Modifier.onClick(onClick: () -> Unit): Modifier + +/** + * Has the interactive effect when clicked on the `androidxCommon` targets. + */ +@ExperimentalFoundationApi +expect fun Modifier.clickable(onClick: () -> Unit): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.kt index e5ca0111..fd772413 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.kt @@ -2,6 +2,8 @@ package com.huanshankeji.compose.foundation import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable +import com.huanshankeji.compose.foundation.layout.BoxScope +import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier @Composable @@ -17,6 +19,11 @@ expect class ScrollState It seems `state` has to be achieved with `DisposableEffect` on JS which can not be set with the Kobweb `Modifier` yet. See https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop. */ +/** + * Note that if the component is a layout, for example a Material card, + * it applies to the target as a whole on `androidx` targets, but applies to its content on JS DOM. + * For consistency on different platforms, [VerticalScrollBox] is recommended over this modifier. + */ expect fun Modifier.verticalScroll( state: ScrollState /* @@ -26,4 +33,24 @@ expect fun Modifier.verticalScroll( */ ): Modifier +/** + * For consistency on different platforms, [HorizontalScrollBox] is recommended over this modifier. + * @see verticalScroll + */ expect fun Modifier.horizontalScroll(state: ScrollState): Modifier + +@Composable +expect fun VerticalScrollBox( + boxModifier: Modifier = Modifier, + contentModifier: Modifier = Modifier, + contentAlignment: Alignment = Alignment.TopStart, + content: @Composable BoxScope.() -> Unit +) + +@Composable +expect fun HorizontalScrollBox( + boxModifier: Modifier = Modifier, + contentModifier: Modifier = Modifier, + contentAlignment: Alignment = Alignment.TopStart, + content: @Composable BoxScope.() -> Unit +) diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/ext/MatchPositionRelativeParentJsOnly.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/ext/MatchPositionRelativeParentJsOnly.kt new file mode 100644 index 00000000..29f54d15 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/ext/MatchPositionRelativeParentJsOnly.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.compose.foundation.ext + +import com.huanshankeji.compose.ui.Modifier + +/* +const val MATCH_POSITION_RELATIVE_PARENT_DEPRECATED_MESSAGE = + "This function's functionality is replaced by `imitateAndroidxLayout`." +*/ + + +//@Deprecated(MATCH_POSITION_RELATIVE_PARENT_DEPRECATED_MESSAGE) +expect fun Modifier.matchPositionRelativeParentJsDom(): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.kt index 97d9a89d..745342a5 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.kt @@ -2,6 +2,7 @@ package com.huanshankeji.compose.foundation.layout import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp @Immutable expect object Arrangement { @@ -37,4 +38,7 @@ expect object Arrangement { @Stable val SpaceAround: HorizontalOrVertical + + @Stable + fun spacedBy(space: Dp): HorizontalOrVertical } diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Intrinsic.kt similarity index 84% rename from compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.kt rename to compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Intrinsic.kt index 52ce3016..7ba4b412 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Intrinsic.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.layout +package com.huanshankeji.compose.foundation.layout import androidx.compose.runtime.Stable import com.huanshankeji.compose.ui.Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Padding.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Padding.kt new file mode 100644 index 00000000..abee1062 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Padding.kt @@ -0,0 +1,94 @@ +package com.huanshankeji.compose.foundation.layout + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.huanshankeji.compose.foundation.layout.ext.innerPadding +import com.huanshankeji.compose.foundation.layout.ext.outerPadding +import com.huanshankeji.compose.ui.Modifier + +private const val PADDING_DEPRECATED_MESSAGE = "Use `outerPadding` or checkout out `innerPadding` instead." + +private const val REPLACE_WITH_PACKAGE = "com.huanshankeji.compose.foundation.layout.ext" +private const val OUTER_PADDING_REPLACE_WITH_IMPORT = "$REPLACE_WITH_PACKAGE.outerPadding" +private const val ABSOLUTE_OUTER_PADDING_REPLACE_WITH_IMPORT = "$REPLACE_WITH_PACKAGE.absoluteOuterPadding" + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Deprecated( + PADDING_DEPRECATED_MESSAGE, + ReplaceWith("this.outerPadding(start, top, end, bottom)", OUTER_PADDING_REPLACE_WITH_IMPORT) +) +@Stable +expect fun Modifier.padding(start: Dp = 0.dp, top: Dp = 0.dp, end: Dp = 0.dp, bottom: Dp = 0.dp): Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Deprecated( + PADDING_DEPRECATED_MESSAGE, + ReplaceWith("this.outerPadding(horizontal, vertical)", OUTER_PADDING_REPLACE_WITH_IMPORT) +) +@Stable +expect fun Modifier.padding(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): Modifier + +/** + * Padding works differently on `androidx.compose` and CSS. + * In `androidx.compose`, the `padding` modifier adds a layer of wrapper around the component, and [order of modifiers matters](https://developer.android.com/develop/ui/compose/modifiers#order-modifier-matters); + * while in CSS, this function delegates to the `margin` CSS properties. + * @see outerPadding + * @see innerPadding + */ +@Deprecated(PADDING_DEPRECATED_MESSAGE, ReplaceWith("this.outerPadding(all)", OUTER_PADDING_REPLACE_WITH_IMPORT)) +@Stable +expect fun Modifier.padding(all: Dp): Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Deprecated( + PADDING_DEPRECATED_MESSAGE, + ReplaceWith("this.outerPadding(paddingValues)", OUTER_PADDING_REPLACE_WITH_IMPORT) +) +@Stable +expect fun Modifier.padding(paddingValues: PaddingValues): Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Deprecated( + PADDING_DEPRECATED_MESSAGE, + ReplaceWith("this.absoluteOuterPadding(left, top, right, bottom)", ABSOLUTE_OUTER_PADDING_REPLACE_WITH_IMPORT) +) +@Stable +expect fun Modifier.absolutePadding(left: Dp = 0.dp, top: Dp = 0.dp, right: Dp = 0.dp, bottom: Dp = 0.dp): Modifier + +// copied and adapted from `androidx.compose.foundation.layout.Padding.kt` + +@Stable +expect abstract class PaddingValues { + @Immutable + class Absolute( + /*@Stable*/ + left: Dp = 0.dp, + /*@Stable*/ + top: Dp = 0.dp, + /*@Stable*/ + right: Dp = 0.dp, + /*@Stable*/ + bottom: Dp = 0.dp + ) : PaddingValues +} + +@Stable +expect fun PaddingValues(all: Dp): PaddingValues + +@Stable +expect fun PaddingValues(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): PaddingValues + +@Stable +expect fun PaddingValues( + start: Dp = 0.dp, top: Dp = 0.dp, end: Dp = 0.dp, bottom: Dp = 0.dp +): PaddingValues diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Size.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Size.kt similarity index 78% rename from compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Size.kt rename to compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Size.kt index d6c73270..ce2c4bec 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Size.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Size.kt @@ -1,9 +1,12 @@ -package com.huanshankeji.compose.layout +package com.huanshankeji.compose.foundation.layout import androidx.annotation.FloatRange import androidx.compose.runtime.Stable import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpSize +import com.huanshankeji.compose.foundation.layout.ext.fillMaxHeightStretch +import com.huanshankeji.compose.foundation.layout.ext.fillMaxSizeStretch +import com.huanshankeji.compose.foundation.layout.ext.fillMaxWidthStretch import com.huanshankeji.compose.ui.Modifier @Stable @@ -36,12 +39,21 @@ expect fun Modifier.sizeIn( maxHeight: Dp = Dp.Unspecified ): Modifier +/** + * @see fillMaxWidthStretch + */ @Stable expect fun Modifier.fillMaxWidth(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f): Modifier +/** + * @see fillMaxHeightStretch + */ @Stable expect fun Modifier.fillMaxHeight(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f): Modifier +/** + * @see fillMaxSizeStretch + */ @Stable expect fun Modifier.fillMaxSize(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/BoxWithConstraints.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/BoxWithConstraints.kt new file mode 100644 index 00000000..21bebe37 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/BoxWithConstraints.kt @@ -0,0 +1,27 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import com.huanshankeji.compose.foundation.ExperimentalFoundationApi +import com.huanshankeji.compose.foundation.layout.BoxScope +import com.huanshankeji.compose.ui.Alignment +import com.huanshankeji.compose.ui.Modifier + +// copied and adapted from `androidx.compose.foundation.layout` + +@ExperimentalFoundationApi +@Composable +//@UiComposable +expect fun BoxWithConstraints( + modifier: Modifier = Modifier, + contentAlignment: Alignment = Alignment.TopStart, + //propagateMinConstraints: Boolean = false, + content: @Composable /*@UiComposable*/ BoxWithConstraintsScope.() -> Unit +) + +@Stable +interface BoxWithConstraintsScope : BoxScope { + val maxWidth: Dp + val maxHeight: Dp +} diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/InnerPadding.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/InnerPadding.kt new file mode 100644 index 00000000..e6d640ec --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/InnerPadding.kt @@ -0,0 +1,43 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.huanshankeji.compose.foundation.background +import com.huanshankeji.compose.foundation.clickable +import com.huanshankeji.compose.foundation.layout.PaddingValues +import com.huanshankeji.compose.foundation.layout.padding +import com.huanshankeji.compose.ui.Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Stable +expect fun Modifier.innerPadding(start: Dp = 0.dp, top: Dp = 0.dp, end: Dp = 0.dp, bottom: Dp = 0.dp): Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Stable +expect fun Modifier.innerPadding(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): Modifier + +/** + * This delegates to the CSS `padding` property on JS DOM and adds the padding inside. + * For consistency on different platforms, please use it as the **last** modifier **after** the other modifiers such as [background] and [clickable]. + * See the KDoc of the [padding] overload with one [Dp] parameter for more information. + * @see outerPadding + */ +@Stable +expect fun Modifier.innerPadding(all: Dp): Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Stable +expect fun Modifier.innerPadding(paddingValues: PaddingValues): Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Stable +expect fun Modifier.absoluteInnerPadding(left: Dp = 0.dp, top: Dp = 0.dp, right: Dp = 0.dp, bottom: Dp = 0.dp): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/OuterPadding.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/OuterPadding.kt new file mode 100644 index 00000000..ffcb90f5 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/OuterPadding.kt @@ -0,0 +1,43 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.huanshankeji.compose.foundation.background +import com.huanshankeji.compose.foundation.clickable +import com.huanshankeji.compose.foundation.layout.PaddingValues +import com.huanshankeji.compose.foundation.layout.padding +import com.huanshankeji.compose.ui.Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Stable +expect fun Modifier.outerPadding(start: Dp = 0.dp, top: Dp = 0.dp, end: Dp = 0.dp, bottom: Dp = 0.dp): Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Stable +expect fun Modifier.outerPadding(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): Modifier + +/** + * This delegates to the CSS `margin` property on JS DOM and adds the padding outside. + * For consistency on different platforms, please use it as the **first** modifier **before** the other modifiers such as [background] and [clickable]. + * See the KDoc of the [padding] overload with one [Dp] parameter for more information. + * @see innerPadding + */ +@Stable +expect fun Modifier.outerPadding(all: Dp): Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Stable +expect fun Modifier.outerPadding(paddingValues: PaddingValues): Modifier + +/** + * See the KDoc of the overload with one [Dp] parameter for platform differences. + */ +@Stable +expect fun Modifier.absoluteOuterPadding(left: Dp = 0.dp, top: Dp = 0.dp, right: Dp = 0.dp, bottom: Dp = 0.dp): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Size.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Size.kt new file mode 100644 index 00000000..1f9e19d4 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Size.kt @@ -0,0 +1,26 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.foundation.layout.fillMaxHeight +import com.huanshankeji.compose.foundation.layout.fillMaxSize +import com.huanshankeji.compose.foundation.layout.fillMaxWidth +import com.huanshankeji.compose.ui.Modifier + +/** + * Similar to [fillMaxWidth] but delegates to the `stretch` CSS value on JS DOM. + * See https://developer.mozilla.org/en-US/docs/Web/CSS/width#stretch. + */ +@Stable +expect fun Modifier.fillMaxWidthStretch(): Modifier + +/** + * Similar to [fillMaxHeight] but delegates to the `stretch` CSS value on JS DOM. + */ +@Stable +expect fun Modifier.fillMaxHeightStretch(): Modifier + +/** + * Similar to [fillMaxSize] but delegates to the `stretch` CSS value on JS DOM. + */ +@Stable +expect fun Modifier.fillMaxSizeStretch(): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.kt index fddc0ad0..912be120 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.kt @@ -2,6 +2,7 @@ package com.huanshankeji.compose.foundation.lazy import androidx.compose.runtime.Composable import com.huanshankeji.compose.foundation.layout.Arrangement +import com.huanshankeji.compose.foundation.layout.PaddingValues import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier @@ -95,7 +96,7 @@ inline fun LazyListScope.itemsIndexed( expect fun LazyRow( modifier: Modifier = Modifier, //state: LazyListState = rememberLazyListState(), - //contentPadding: PaddingValues = PaddingValues(0.dp), + contentPadding: PaddingValues? = null, reverseLayout: Boolean = false, horizontalArrangement: Arrangement.Horizontal = if (!reverseLayout) Arrangement.Start else Arrangement.End, @@ -112,7 +113,7 @@ expect fun LazyRow( expect fun LazyColumn( modifier: Modifier = Modifier, //state: LazyListState = rememberLazyListState(), - //contentPadding: PaddingValues = PaddingValues(0.dp), + contentPadding: PaddingValues? = null, reverseLayout: Boolean = false, verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.kt index 09b4e581..4abe8dd0 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.kt @@ -2,9 +2,15 @@ package com.huanshankeji.compose.foundation.text import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.ColorProducer +@Deprecated( + "This is not needed as there in a `TaglessBasicText` composable. Use the one below.", + level = DeprecationLevel.HIDDEN +) @Composable -expect fun BasicText(text: String) +fun BasicText(text: String) = + BasicText(text, Modifier, null) @Composable -expect fun BasicText(text: String, modifier: Modifier) +expect fun BasicText(text: String, modifier: Modifier = Modifier, color: ColorProducer? = null) diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.kt index 8965a575..90b64270 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.kt @@ -1,9 +1,26 @@ package com.huanshankeji.compose.foundation.text.ext import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.text.BasicText +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.ColorProducer + +const val INLINE_TEXT_DEPRECATED_MESSAGE = "Both text with or without a `span` ta can be inline." + +@Deprecated(INLINE_TEXT_DEPRECATED_MESSAGE, ReplaceWith("TaglessBasicText(text)")) +@Composable +fun InlineBasicText(text: String) = + TaglessBasicText(text) + +/** + * Delegates to raw text without any tag element on JS / Compose HTML. + */ +@Composable +expect fun TaglessBasicText(text: String) /** - * Delegates to raw inline text without any element on JS / Compose HTML. + * An alias for [BasicText]. */ @Composable -expect fun InlineBasicText(text: String) +fun SpanBasicText(text: String, modifier: Modifier = Modifier, color: ColorProducer? = null) = + BasicText(text, modifier, color) diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Padding.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Padding.kt deleted file mode 100644 index a5e753e1..00000000 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Padding.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.huanshankeji.compose.layout - -import androidx.compose.runtime.Stable -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import com.huanshankeji.compose.ui.Modifier - -@Stable -expect fun Modifier.padding(start: Dp = 0.dp, top: Dp = 0.dp, end: Dp = 0.dp, bottom: Dp = 0.dp): Modifier - -@Stable -expect fun Modifier.padding(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): Modifier - -@Stable -expect fun Modifier.padding(all: Dp): Modifier - -@Stable -expect fun Modifier.absolutePadding(left: Dp = 0.dp, top: Dp = 0.dp, right: Dp = 0.dp, bottom: Dp = 0.dp): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/ext/Hidden.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/ext/Hidden.kt new file mode 100644 index 00000000..5a5038ff --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/ext/Hidden.kt @@ -0,0 +1,15 @@ +package com.huanshankeji.compose.layout.ext + +import com.huanshankeji.compose.ui.Modifier + +expect fun Modifier.hidden(): Modifier + +/** + * An alias for [hidden]. + */ +fun Modifier.reserveSpace() = hidden() + +/** + * An alias for [hidden]. + */ +fun Modifier.invisible() = hidden() diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/Modifier.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/Modifier.kt index e40d8887..299e8956 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/Modifier.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/Modifier.kt @@ -5,4 +5,10 @@ expect interface Modifier { open infix fun then(other: Modifier): Modifier interface Element : Modifier companion object : Modifier -} \ No newline at end of file +} + +/** + * This serves as a shortcut for code completion and an alternative name when you prefer another type taking the name `Modifier` in a file. + * In most cases, it's recommended to use [Modifier] directly. + */ +typealias CommonModifier = Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/draw/Alpha.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/draw/Alpha.kt new file mode 100644 index 00000000..4181962e --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/draw/Alpha.kt @@ -0,0 +1,5 @@ +package com.huanshankeji.compose.ui.draw + +import com.huanshankeji.compose.ui.Modifier + +expect fun Modifier.alpha(alpha: Float): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/draw/Rotate.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/draw/Rotate.kt new file mode 100644 index 00000000..149b4e3b --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/draw/Rotate.kt @@ -0,0 +1,7 @@ +package com.huanshankeji.compose.ui.draw + +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier + +@Stable +expect fun Modifier.rotate(degrees: Float): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.kt index 3ffa8e1d..28744984 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.kt @@ -110,3 +110,7 @@ expect fun Color.luminance(): Float @Stable @ColorInt expect fun Color.toArgb(): Int + +fun interface ColorProducer { + operator fun invoke(): Color +} diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/browser/Browser.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/browser/Browser.kt new file mode 100644 index 00000000..328f0e7a --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/browser/Browser.kt @@ -0,0 +1,18 @@ +package com.huanshankeji.browser + +import kotlinx.browser.window + +// TODO consider moving to "kotlin-common" + +enum class Browser { + Webkit, Mozilla +} + +val browser = run { + val userAgent = window.navigator.userAgent + when { + userAgent.contains("AppleWebKit") -> Browser.Webkit + userAgent.contains("Mozilla") -> Browser.Mozilla + else -> null + } +} diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/OnClick.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/OnClick.js.kt index 2856d5fb..b9786a5a 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/OnClick.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/OnClick.js.kt @@ -3,5 +3,10 @@ package com.huanshankeji.compose.foundation import com.huanshankeji.compose.ui.Modifier import com.varabyte.kobweb.compose.ui.modifiers.onClick +@ExperimentalFoundationApi actual fun Modifier.onClick(onClick: () -> Unit): Modifier = platformModify { onClick { onClick() } } + +@ExperimentalFoundationApi +actual fun Modifier.clickable(onClick: () -> Unit): Modifier = + platformModify { onClick { onClick() } } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/Scroll.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/Scroll.js.kt index 4e8a12b7..f5ed946d 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/Scroll.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/Scroll.js.kt @@ -1,10 +1,16 @@ package com.huanshankeji.compose.foundation import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import com.huanshankeji.compose.foundation.ext.css.horizontalScroll import com.huanshankeji.compose.foundation.ext.css.verticalScroll +import com.huanshankeji.compose.foundation.layout.Box +import com.huanshankeji.compose.foundation.layout.BoxScope +import com.huanshankeji.compose.foundation.layout.ext.KobwebBox +import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.PlatformModifier +import com.huanshankeji.kobweb.compose.ui.modifiers.imitateAndroidxLayout import com.varabyte.kobweb.compose.ui.styleModifier fun PlatformModifier.verticalScroll() = @@ -13,15 +19,16 @@ fun PlatformModifier.verticalScroll() = fun PlatformModifier.horizontalScroll() = styleModifier { horizontalScroll() } -val verticalScrollPlatformModifier = PlatformModifier.verticalScroll() -val horizontalScrollPlatformModifier = PlatformModifier.horizontalScroll() +val imitateAndroidxLayoutVerticalScrollPlatformModifier = PlatformModifier.imitateAndroidxLayout().verticalScroll() +val imitateAndroidxLayoutHorizontalScrollPlatformModifier = PlatformModifier.imitateAndroidxLayout().horizontalScroll() @Composable actual fun rememberScrollState(initial: Int): ScrollState = ScrollState -actual typealias ScrollState = Unit +@Stable +actual object ScrollState actual fun Modifier.verticalScroll(state: ScrollState): Modifier = platformModify { verticalScroll() } @@ -30,3 +37,24 @@ actual fun Modifier.verticalScroll(state: ScrollState): Modifier = actual fun Modifier.horizontalScroll(state: ScrollState): Modifier = platformModify { horizontalScroll() } +@Composable +actual fun VerticalScrollBox( + boxModifier: Modifier, + contentModifier: Modifier, + contentAlignment: Alignment, + content: @Composable BoxScope.() -> Unit +) = + Box(boxModifier.verticalScroll(rememberScrollState())) { + KobwebBox(contentModifier, contentAlignment, content) + } + +@Composable +actual fun HorizontalScrollBox( + boxModifier: Modifier, + contentModifier: Modifier, + contentAlignment: Alignment, + content: @Composable BoxScope.() -> Unit +) = + Box(boxModifier.horizontalScroll(rememberScrollState())) { + KobwebBox(contentModifier, contentAlignment, content) + } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/ext/MatchPositionRelativeParentJsDom.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/ext/MatchPositionRelativeParentJsDom.js.kt new file mode 100644 index 00000000..34091886 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/ext/MatchPositionRelativeParentJsDom.js.kt @@ -0,0 +1,23 @@ +package com.huanshankeji.compose.foundation.ext + +import com.huanshankeji.compose.ui.Modifier +import com.varabyte.kobweb.compose.ui.attrsModifier +import org.jetbrains.compose.web.css.* + +fun StyleScope.matchPositionRelativeParent() { + position(Position.Absolute) + left(0.px) + top(0.px) + right(0.px) + bottom(0.px) + // Also considering using `inset` (https://developer.mozilla.org/en-US/docs/Web/CSS/inset) +} + +actual fun Modifier.matchPositionRelativeParentJsDom(): Modifier = + platformModify { + attrsModifier { + style { + matchPositionRelativeParent() + } + } + } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.js.kt index f4e93fb5..a62abf5c 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Arrangement.js.kt @@ -2,19 +2,32 @@ package com.huanshankeji.compose.foundation.layout import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import com.huanshankeji.compose.foundation.layout.Arrangement.CommonArrangement +import com.huanshankeji.compose.ui.PlatformModifier +import com.huanshankeji.compose.ui.unit.toPx +import com.varabyte.kobweb.compose.foundation.layout.Arrangement +import com.varabyte.kobweb.compose.ui.styleModifier +import org.jetbrains.compose.web.css.StyleScope +import org.jetbrains.compose.web.css.gap import com.varabyte.kobweb.compose.foundation.layout.Arrangement as PlatformArrangement @Immutable actual object Arrangement { @Stable - actual interface Horizontal { + interface CommonArrangement { + fun StyleScope.styles() {} + } + + @Stable + actual interface Horizontal : CommonArrangement { val platformValue: PlatformArrangement.Horizontal class Impl(override val platformValue: PlatformArrangement.Horizontal) : Horizontal } @Stable - actual interface Vertical { + actual interface Vertical : CommonArrangement { val platformValue: PlatformArrangement.Vertical class Impl(override val platformValue: PlatformArrangement.Vertical) : Vertical @@ -25,6 +38,13 @@ actual object Arrangement { override val platformValue: PlatformArrangement.HorizontalOrVertical class Impl(override val platformValue: PlatformArrangement.HorizontalOrVertical) : HorizontalOrVertical + + abstract class FromStyleImpl : HorizontalOrVertical { + override val platformValue: Arrangement.HorizontalOrVertical + get() = Arrangement.FromStyle + + abstract override fun StyleScope.styles() + } } @Stable @@ -50,4 +70,17 @@ actual object Arrangement { @Stable actual val SpaceAround: HorizontalOrVertical = HorizontalOrVertical.Impl(PlatformArrangement.SpaceAround) -} \ No newline at end of file + + @Stable + actual fun spacedBy(space: Dp): HorizontalOrVertical = + object : HorizontalOrVertical.FromStyleImpl() { + override fun StyleScope.styles() { + gap(space.toPx()) + } + } +} + +fun PlatformModifier.stylesFrom(arrangement: CommonArrangement) = + styleModifier { + with(arrangement) { styles() } + } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Box.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Box.js.kt index 57dc24cf..d4181be6 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Box.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Box.js.kt @@ -3,12 +3,13 @@ package com.huanshankeji.compose.foundation.layout import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable +import com.huanshankeji.compose.foundation.layout.ext.KobwebBox import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.PlatformModifier -import com.huanshankeji.kobweb.compose.ui.modifiers.sizeFitContent +import com.huanshankeji.compose.ui.toCommonModifier +import com.huanshankeji.kobweb.compose.ui.modifiers.imitateAndroidxLayout import com.varabyte.kobweb.compose.foundation.layout.LayoutScopeMarker -import com.varabyte.kobweb.compose.foundation.layout.Box as PlatformBox import com.varabyte.kobweb.compose.foundation.layout.BoxScope as PlatformBoxScope @Composable @@ -16,17 +17,16 @@ actual fun Box( modifier: Modifier, contentAlignment: Alignment, content: @Composable BoxScope.() -> Unit -) { - AddKobwebComposeStyleSheet() - PlatformBox( - PlatformModifier.sizeFitContent().then(modifier.platformModifier), - contentAlignment.platformValue, - ) { BoxScope.Impl(this).content() } -} +) = + KobwebBox( + PlatformModifier.imitateAndroidxLayout().toCommonModifier().then(modifier), + contentAlignment, + content + ) @Composable actual fun Box(modifier: Modifier) = - PlatformBox(PlatformModifier.sizeFitContent().then(modifier.platformModifier)) + KobwebBox(PlatformModifier.imitateAndroidxLayout().then(modifier.platformModifier)) @LayoutScopeMarker @Immutable diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.js.kt index be3b9ea2..41846b1a 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.js.kt @@ -6,7 +6,7 @@ import androidx.compose.runtime.Stable import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.PlatformModifier -import com.huanshankeji.kobweb.compose.ui.modifiers.sizeFitContent +import com.huanshankeji.kobweb.compose.ui.modifiers.imitateAndroidxLayout import com.varabyte.kobweb.compose.foundation.layout.LayoutScopeMarker @Composable @@ -19,7 +19,8 @@ actual fun Column( AddKobwebComposeStyleSheet() com.varabyte.kobweb.compose.foundation.layout.Column( PlatformModifier - .sizeFitContent() // "fit-content" is added to make it consistent with the `androidx` one + .imitateAndroidxLayout() // "fit-content" is added to make it consistent with the `androidx` one + .stylesFrom(verticalArrangement) .then(modifier.platformModifier), verticalArrangement.platformValue, horizontalAlignment.platformValue, @@ -41,7 +42,7 @@ actual interface ColumnScope { @FloatRange(from = 0.0, fromInclusive = false) weight: Float ): Modifier = - with(platformValue) { platformModify { weight(weight) } } + with(platformValue) { platformModify { flexBasis0().weight(weight) } } @Stable actual fun Modifier.align(alignment: Alignment.Horizontal): Modifier = diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/FlexBasis.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/FlexBasis.kt new file mode 100644 index 00000000..d669104e --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/FlexBasis.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.foundation.layout + +import com.huanshankeji.compose.ui.PlatformModifier +import com.varabyte.kobweb.compose.ui.modifiers.flexBasis +import org.jetbrains.compose.web.css.px + +fun PlatformModifier.flexBasis0() = + flexBasis(0.px) diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Intrinsic.js.kt similarity index 94% rename from compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.js.kt rename to compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Intrinsic.js.kt index 573b73c3..df8185a8 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Intrinsic.js.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.layout +package com.huanshankeji.compose.foundation.layout import androidx.compose.runtime.Stable import com.huanshankeji.compose.ui.Modifier diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Padding.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Padding.js.kt new file mode 100644 index 00000000..f099d0f5 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Padding.js.kt @@ -0,0 +1,69 @@ +package com.huanshankeji.compose.foundation.layout + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import com.huanshankeji.compose.foundation.layout.ext.absoluteOuterPadding +import com.huanshankeji.compose.foundation.layout.ext.outerPadding +import com.huanshankeji.compose.ui.Modifier + +@Stable +actual fun Modifier.padding(start: Dp, top: Dp, end: Dp, bottom: Dp): Modifier = + outerPadding(start, top, end, bottom) + +@Stable +actual fun Modifier.padding(horizontal: Dp, vertical: Dp): Modifier = + outerPadding(horizontal, vertical) + +@Stable +actual fun Modifier.padding(all: Dp): Modifier = + outerPadding(all) + +@Stable +actual fun Modifier.padding(paddingValues: PaddingValues): Modifier = + outerPadding(paddingValues) + +@Stable +actual fun Modifier.absolutePadding(left: Dp, top: Dp, right: Dp, bottom: Dp): Modifier = + absoluteOuterPadding(left, top, right, bottom) + +// copied and adapted from `androidx.compose.foundation.layout.Padding.kt` + +@Stable +actual abstract class PaddingValues internal constructor() { + @Immutable + actual class Absolute actual constructor( + @Stable + val left: Dp, + @Stable + val top: Dp, + @Stable + val right: Dp, + @Stable + val bottom: Dp + ) : PaddingValues() +} + +@Stable +actual fun PaddingValues(all: Dp): PaddingValues = + PaddingValuesImpl(all, all, all, all) + +@Stable +actual fun PaddingValues(horizontal: Dp, vertical: Dp): PaddingValues = + PaddingValuesImpl(horizontal, vertical, horizontal, vertical) + +@Stable +actual fun PaddingValues(start: Dp, top: Dp, end: Dp, bottom: Dp): PaddingValues = + PaddingValuesImpl(start, top, end, bottom) + +@Immutable +internal class PaddingValuesImpl( + @Stable + val start: Dp, + @Stable + val top: Dp, + @Stable + val end: Dp, + @Stable + val bottom: Dp +) : PaddingValues() diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.js.kt index 04b1e3a5..f13c5e4c 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.js.kt @@ -6,7 +6,7 @@ import androidx.compose.runtime.Stable import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.PlatformModifier -import com.huanshankeji.kobweb.compose.ui.modifiers.sizeFitContent +import com.huanshankeji.kobweb.compose.ui.modifiers.imitateAndroidxLayout import com.varabyte.kobweb.compose.foundation.layout.LayoutScopeMarker @Composable @@ -19,7 +19,8 @@ actual fun Row( AddKobwebComposeStyleSheet() com.varabyte.kobweb.compose.foundation.layout.Row( PlatformModifier - .sizeFitContent() + .imitateAndroidxLayout() + .stylesFrom(horizontalArrangement) .then(modifier.platformModifier), horizontalArrangement.platformValue, verticalAlignment.platformValue, @@ -41,7 +42,7 @@ actual interface RowScope { @FloatRange(from = 0.0, fromInclusive = false) weight: Float ): Modifier = - with(platformValue) { platformModify { weight(weight) } } + with(platformValue) { platformModify { flexBasis0().weight(weight) } } @Stable actual fun Modifier.align(alignment: Alignment.Vertical): Modifier = diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/Size.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Size.js.kt similarity index 95% rename from compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/Size.js.kt rename to compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Size.js.kt index 124ec2db..76ffe2c4 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/Size.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Size.js.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.layout +package com.huanshankeji.compose.foundation.layout import androidx.annotation.FloatRange import androidx.compose.runtime.Stable @@ -6,7 +6,6 @@ import androidx.compose.ui.unit.Dp import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.unit.toPercent import com.huanshankeji.compose.ui.unit.toPx -import com.huanshankeji.kobweb.compose.ui.modifiers.size import com.huanshankeji.kobweb.compose.ui.modifiers.sizeFitContent import com.varabyte.kobweb.compose.css.Height import com.varabyte.kobweb.compose.css.Width diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Box.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Box.js.kt new file mode 100644 index 00000000..4609c153 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Box.js.kt @@ -0,0 +1,53 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.AddKobwebComposeStyleSheet +import com.huanshankeji.compose.foundation.layout.BoxScope +import com.huanshankeji.compose.ui.CommonModifier +import com.huanshankeji.compose.ui.PlatformModifier +import com.huanshankeji.kobweb.compose.ui.modifiers.imitateAndroidxLayout +import com.varabyte.kobweb.compose.dom.ElementRefScope +import com.varabyte.kobweb.compose.foundation.layout.Box +import com.varabyte.kobweb.compose.ui.toAttrs +import org.jetbrains.compose.web.dom.ContentBuilder +import org.jetbrains.compose.web.dom.Div +import org.w3c.dom.HTMLDivElement +import org.w3c.dom.HTMLElement +import com.huanshankeji.compose.foundation.layout.BoxScope as CommonBoxScope +import com.huanshankeji.compose.ui.Alignment as CommonAlignment +import com.varabyte.kobweb.compose.foundation.layout.BoxScope as PlatformBoxScope +import com.varabyte.kobweb.compose.ui.Alignment as PlatformAlignment + +/** + * Delegates to [Box] without [com.varabyte.kobweb.compose.ui.Modifier.imitateAndroidxLayout]. + */ +@Composable +fun KobwebBox( + modifier: PlatformModifier = PlatformModifier, + contentAlignment: PlatformAlignment = PlatformAlignment.TopStart, + ref: ElementRefScope? = null, + content: @Composable PlatformBoxScope.() -> Unit = {} +) { + AddKobwebComposeStyleSheet() + Box(modifier, contentAlignment, ref, content) +} + +@Composable +fun KobwebBox( + modifier: CommonModifier = CommonModifier, + contentAlignment: CommonAlignment, + content: @Composable CommonBoxScope.() -> Unit = {} +) = + KobwebBox(modifier.platformModifier, contentAlignment.platformValue) { + BoxScope.Impl(this).content() + } + +/** + * Delegates to [Div] without [com.varabyte.kobweb.compose.ui.Modifier.imitateAndroidxLayout]. + */ +@Composable +fun DivBox( + modifier: PlatformModifier = PlatformModifier, + content: ContentBuilder? = null +) = + Div(modifier.toAttrs(), content) diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/BoxWithConstraints.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/BoxWithConstraints.js.kt new file mode 100644 index 00000000..b43dee99 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/BoxWithConstraints.js.kt @@ -0,0 +1,47 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.* +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.huanshankeji.compose.foundation.ExperimentalFoundationApi +import com.huanshankeji.compose.foundation.layout.Box +import com.huanshankeji.compose.ui.Alignment +import com.huanshankeji.compose.ui.Modifier +import com.varabyte.kobweb.compose.foundation.layout.BoxScope +import com.varabyte.kobweb.compose.ui.attrsModifier + +@ExperimentalFoundationApi +@Composable +actual fun BoxWithConstraints( + modifier: Modifier, + contentAlignment: Alignment, + content: @Composable BoxWithConstraintsScope.() -> Unit +) { + var clientSize by remember { mutableStateOf(null) } + Box( + Modifier.platformModify { + attrsModifier { + ref { + clientSize = ClientSize(it.clientWidth, it.clientHeight) + onDispose { clientSize = null } + } + } + }.fillMaxSizeStretch() + .then(modifier), + contentAlignment + ) { + clientSize?.let { + // TODO extra conversions might be needed in some cases when converting to `dp` + BoxWithConstraintsScopeImpl(platformBoxScope, it.clientWidth.dp, it.clientHeight.dp).content() + } + } +} + +@Stable +class BoxWithConstraintsScopeImpl( + override val platformBoxScope: BoxScope, + override val maxWidth: Dp, + override val maxHeight: Dp +) : BoxWithConstraintsScope + +private class ClientSize(val clientWidth: Int, val clientHeight: Int) diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/InnerPadding.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/InnerPadding.js.kt new file mode 100644 index 00000000..87955c59 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/InnerPadding.js.kt @@ -0,0 +1,40 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import com.huanshankeji.compose.foundation.layout.PaddingValues +import com.huanshankeji.compose.foundation.layout.PaddingValuesImpl +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.unit.toPx +import com.varabyte.kobweb.compose.ui.modifiers.padding +import com.varabyte.kobweb.compose.ui.modifiers.paddingInline + +@Stable +actual fun Modifier.innerPadding(start: Dp, top: Dp, end: Dp, bottom: Dp): Modifier = + platformModify { + paddingInline(start.toPx(), end.toPx()) + .padding { + top(top.toPx()) + bottom(bottom.toPx()) + } + } + +@Stable +actual fun Modifier.innerPadding(horizontal: Dp, vertical: Dp): Modifier = + platformModify { padding(vertical.toPx(), horizontal.toPx()) } + +@Stable +actual fun Modifier.innerPadding(all: Dp): Modifier = + platformModify { padding(all.toPx()) } + +@Stable +actual fun Modifier.innerPadding(paddingValues: PaddingValues): Modifier = + when (paddingValues) { + is PaddingValues.Absolute -> with(paddingValues) { absoluteInnerPadding(left, top, right, bottom) } + is PaddingValuesImpl -> with(paddingValues) { innerPadding(start, top, end, bottom) } + else -> throw IllegalArgumentException(paddingValues.toString()) + } + +@Stable +actual fun Modifier.absoluteInnerPadding(left: Dp, top: Dp, right: Dp, bottom: Dp): Modifier = + platformModify { padding(top.toPx(), right.toPx(), bottom.toPx(), left.toPx()) } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/OuterPadding.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/OuterPadding.js.kt new file mode 100644 index 00000000..55a79422 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/OuterPadding.js.kt @@ -0,0 +1,40 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.Stable +import androidx.compose.ui.unit.Dp +import com.huanshankeji.compose.foundation.layout.PaddingValues +import com.huanshankeji.compose.foundation.layout.PaddingValuesImpl +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.unit.toPx +import com.varabyte.kobweb.compose.ui.modifiers.margin +import com.varabyte.kobweb.compose.ui.modifiers.marginInline + +@Stable +actual fun Modifier.outerPadding(start: Dp, top: Dp, end: Dp, bottom: Dp): Modifier = + platformModify { + marginInline(start.toPx(), end.toPx()) + .margin { + top(top.toPx()) + bottom(bottom.toPx()) + } + } + +@Stable +actual fun Modifier.outerPadding(horizontal: Dp, vertical: Dp): Modifier = + platformModify { margin(vertical.toPx(), horizontal.toPx()) } + +@Stable +actual fun Modifier.outerPadding(all: Dp): Modifier = + platformModify { margin(all.toPx()) } + +@Stable +actual fun Modifier.outerPadding(paddingValues: PaddingValues): Modifier = + when (paddingValues) { + is PaddingValues.Absolute -> with(paddingValues) { absoluteOuterPadding(left, top, right, bottom) } + is PaddingValuesImpl -> with(paddingValues) { outerPadding(start, top, end, bottom) } + else -> throw IllegalArgumentException(paddingValues.toString()) + } + +@Stable +actual fun Modifier.absoluteOuterPadding(left: Dp, top: Dp, right: Dp, bottom: Dp): Modifier = + platformModify { margin(top.toPx(), right.toPx(), bottom.toPx(), left.toPx()) } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Size.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Size.js.kt new file mode 100644 index 00000000..97087748 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/ext/Size.js.kt @@ -0,0 +1,50 @@ +package com.huanshankeji.compose.foundation.layout.ext + +import androidx.compose.runtime.Stable +import com.huanshankeji.browser.Browser +import com.huanshankeji.browser.browser +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.web.css.height +import com.huanshankeji.compose.web.css.width +import com.varabyte.kobweb.compose.ui.styleModifier +import org.jetbrains.compose.web.css.StyleScope + +private const val CSS_STRETCH_VALUE = "stretch" // This does not work on Chrome. +private const val CSS_WEBKIT_STRETCH_VALUE = "-webkit-fill-available" +private const val CSS_MOZ_STRETCH_VALUE = "-moz-available" +val cssWidthStretchValueBrowserDependent = + when (browser) { + Browser.Webkit -> CSS_WEBKIT_STRETCH_VALUE + Browser.Mozilla -> CSS_MOZ_STRETCH_VALUE + null -> "" + } + +val cssHeightStretchValueBrowserDependent = + when (browser) { + Browser.Webkit -> CSS_WEBKIT_STRETCH_VALUE + Browser.Mozilla -> "100%" // Setting `CSS_MOZ_STRETCH_VALUE` for `height` seems to be not available on Firefox. See https://developer.mozilla.org/en-US/docs/Web/CSS/height#browser_compatibility. + null -> "" + } + +fun StyleScope.widthStretch() = + width(cssWidthStretchValueBrowserDependent) + +fun StyleScope.heightStretch() = + height(cssHeightStretchValueBrowserDependent) + +@Stable +actual fun Modifier.fillMaxWidthStretch(): Modifier = + platformModify { styleModifier { widthStretch() } } + +@Stable +actual fun Modifier.fillMaxHeightStretch(): Modifier = + platformModify { styleModifier { heightStretch() } } + +@Stable +actual fun Modifier.fillMaxSizeStretch(): Modifier = + platformModify { + styleModifier { + widthStretch() + heightStretch() + } + } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.js.kt index 73a7e70b..83413b51 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.js.kt @@ -1,14 +1,13 @@ package com.huanshankeji.compose.foundation.lazy import androidx.compose.runtime.Composable -import com.huanshankeji.compose.foundation.horizontalScrollPlatformModifier -import com.huanshankeji.compose.foundation.layout.Arrangement -import com.huanshankeji.compose.foundation.layout.Column -import com.huanshankeji.compose.foundation.layout.Row -import com.huanshankeji.compose.foundation.verticalScrollPlatformModifier +import com.huanshankeji.compose.foundation.imitateAndroidxLayoutHorizontalScrollPlatformModifier +import com.huanshankeji.compose.foundation.imitateAndroidxLayoutVerticalScrollPlatformModifier +import com.huanshankeji.compose.foundation.layout.* import com.huanshankeji.compose.runtime.DeferredComposableRunner import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.PlatformModifier import com.huanshankeji.compose.ui.toCommonModifier /* @@ -45,16 +44,26 @@ actual class LazyListScope { } } +private fun commonLazyModifier( + scrollPlatformModifier: PlatformModifier, + contentPadding: PaddingValues?, + modifier: Modifier +) = scrollPlatformModifier.toCommonModifier().run { + if (contentPadding !== null) padding(contentPadding) + else this +}.then(modifier) + @Composable actual fun LazyRow( modifier: Modifier, + contentPadding: PaddingValues?, reverseLayout: Boolean, // This parameter is not used yet but affects the arrangement in the default argument of the corresponding `expect` function. horizontalArrangement: Arrangement.Horizontal, verticalAlignment: Alignment.Vertical, content: LazyListScope.() -> Unit ) = Row( - horizontalScrollPlatformModifier.then(modifier.platformModifier).toCommonModifier(), + commonLazyModifier(imitateAndroidxLayoutHorizontalScrollPlatformModifier, contentPadding, modifier), horizontalArrangement, verticalAlignment ) { @@ -64,13 +73,14 @@ actual fun LazyRow( @Composable actual fun LazyColumn( modifier: Modifier, + contentPadding: PaddingValues?, reverseLayout: Boolean, // This parameter is not used yet but affects the arrangement in the default argument of the corresponding `expect` function. verticalArrangement: Arrangement.Vertical, horizontalAlignment: Alignment.Horizontal, content: LazyListScope.() -> Unit ) = Column( - verticalScrollPlatformModifier.then(modifier.platformModifier).toCommonModifier(), + commonLazyModifier(imitateAndroidxLayoutVerticalScrollPlatformModifier, contentPadding, modifier), verticalArrangement, horizontalAlignment ) { diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.js.kt index 945d09ee..97e02598 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.js.kt @@ -2,9 +2,9 @@ package com.huanshankeji.compose.foundation.lazy import androidx.annotation.FloatRange import androidx.compose.runtime.Stable -import com.huanshankeji.compose.layout.fillMaxHeight -import com.huanshankeji.compose.layout.fillMaxSize -import com.huanshankeji.compose.layout.fillMaxWidth +import com.huanshankeji.compose.foundation.layout.fillMaxHeight +import com.huanshankeji.compose.foundation.layout.fillMaxSize +import com.huanshankeji.compose.foundation.layout.fillMaxWidth import com.huanshankeji.compose.ui.Modifier @Stable diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.js.kt index b038591c..869dc8b1 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/BasicText.js.kt @@ -2,7 +2,8 @@ package com.huanshankeji.compose.foundation.text import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier -import com.huanshankeji.compose.ui.toAttrs +import com.huanshankeji.compose.ui.graphics.ColorProducer +import com.huanshankeji.compose.ui.graphics.toAttrsWithColor import org.jetbrains.compose.web.dom.Span import org.jetbrains.compose.web.dom.Text @@ -12,9 +13,5 @@ When using `com.varabyte.kobweb.silk.components.text.SpanText`: */ @Composable -actual fun BasicText(text: String) = - Span { Text(text) } - -@Composable -actual fun BasicText(text: String, modifier: Modifier) = - Span(modifier.toAttrs()) { Text(text) } +actual fun BasicText(text: String, modifier: Modifier, color: ColorProducer?) = + Span(modifier.toAttrsWithColor(color)) { Text(text) } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.js.kt index 5f5b2f47..665a2ee0 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/ext/Text.js.kt @@ -1,8 +1,18 @@ package com.huanshankeji.compose.foundation.text.ext import androidx.compose.runtime.Composable +import androidx.compose.runtime.InternalComposeApi +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.Color +import com.huanshankeji.compose.ui.graphics.toAttrsWithColor +import org.jetbrains.compose.web.dom.Span import org.jetbrains.compose.web.dom.Text @Composable -actual fun InlineBasicText(text: String) = +actual fun TaglessBasicText(text: String) = Text(text) + +@InternalComposeApi +@Composable +fun CommonBasicText(text: String, modifier: Modifier, color: Color?) = + Span(modifier.toAttrsWithColor(color)) { Text(text) } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/Padding.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/Padding.js.kt deleted file mode 100644 index 5d8718c9..00000000 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/Padding.js.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.huanshankeji.compose.layout - -import androidx.compose.runtime.Stable -import androidx.compose.ui.unit.Dp -import com.huanshankeji.compose.ui.Modifier -import com.huanshankeji.compose.ui.unit.toPx -import com.varabyte.kobweb.compose.ui.modifiers.margin -import com.varabyte.kobweb.compose.ui.modifiers.marginInline - -@Stable -actual fun Modifier.padding(start: Dp, top: Dp, end: Dp, bottom: Dp): Modifier = - platformModify { - marginInline(start.toPx(), end.toPx()) - .margin { - top(top.toPx()) - bottom(bottom.toPx()) - } - } - -@Stable -actual fun Modifier.padding(horizontal: Dp, vertical: Dp): Modifier = - platformModify { margin(vertical.toPx(), horizontal.toPx()) } - -@Stable -actual fun Modifier.padding(all: Dp): Modifier = - platformModify { margin(all.toPx()) } - -@Stable -actual fun Modifier.absolutePadding(left: Dp, top: Dp, right: Dp, bottom: Dp): Modifier = - platformModify { margin(top.toPx(), right.toPx(), bottom.toPx(), left.toPx()) } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/ext/Hidden.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/ext/Hidden.js.kt new file mode 100644 index 00000000..ae88c772 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/ext/Hidden.js.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.layout.ext + +import com.huanshankeji.compose.ui.Modifier +import com.varabyte.kobweb.compose.css.Visibility +import com.varabyte.kobweb.compose.ui.modifiers.visibility + +actual fun Modifier.hidden(): Modifier = + platformModify { visibility(Visibility.Hidden) } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/draw/Alpha.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/draw/Alpha.js.kt new file mode 100644 index 00000000..b4ed3fd2 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/draw/Alpha.js.kt @@ -0,0 +1,7 @@ +package com.huanshankeji.compose.ui.draw + +import com.huanshankeji.compose.ui.Modifier +import com.varabyte.kobweb.compose.ui.modifiers.opacity + +actual fun Modifier.alpha(alpha: Float): Modifier = + platformModify { opacity(alpha) } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/draw/Rotate.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/draw/Rotate.js.kt new file mode 100644 index 00000000..cde3a411 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/draw/Rotate.js.kt @@ -0,0 +1,11 @@ +package com.huanshankeji.compose.ui.draw + +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier +import com.varabyte.kobweb.compose.css.rotate +import com.varabyte.kobweb.compose.ui.styleModifier +import org.jetbrains.compose.web.css.deg + +@Stable +actual fun Modifier.rotate(degrees: Float): Modifier = + platformModify { styleModifier { rotate(degrees.deg) } } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.js.kt index 8a68a73e..5000effb 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/graphics/Color.js.kt @@ -4,7 +4,12 @@ import androidx.annotation.ColorInt import androidx.annotation.IntRange import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs import com.varabyte.kobweb.compose.ui.graphics.luminance +import org.jetbrains.compose.web.attributes.AttrsScope +import org.jetbrains.compose.web.css.StyleScope +import org.jetbrains.compose.web.css.color import com.varabyte.kobweb.compose.ui.graphics.Color as PlatformColor import com.varabyte.kobweb.compose.ui.graphics.Colors as PlatformColors @@ -90,3 +95,49 @@ actual fun Color.luminance(): Float = @Stable actual fun Color.toArgb(): Int = platformValue.toRgb().value + +fun StyleScope.applyStyle(color: ColorProducer) { + color(color().platformValue) +} + + +// TODO context receivers +fun Modifier.toAttrsWithColor(color: Color?): AttrsScope<*>.() -> Unit = + toAttrs(color?.let { + { + style { + color(it.platformValue) + } + } + }) + +/* +fun ColorProducer.toStyle(): StyleScope.() -> Unit = + { + color(this@toStyle().platformValue) + } +*/ + +// TODO context receivers +fun AttrsScope<*>.applyAttrs(color: ColorProducer) { + style { + applyStyle(color) + } +} + +/* +fun ColorProducer.toAttrs(): AttrsScope<*>.() -> Unit = + { + style { + toStyle()() + } + } +*/ + + +fun ColorProducer?.toNullableAttrs(): (AttrsScope<*>.() -> Unit)? = + this?.let { { applyAttrs(it) } } + +// TODO context receivers +fun Modifier.toAttrsWithColor(color: ColorProducer?): AttrsScope<*>.() -> Unit = + toAttrs(color.toNullableAttrs()) diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/kobweb/compose/ui/modifiers/Layout.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/kobweb/compose/ui/modifiers/Layout.kt new file mode 100644 index 00000000..f449cb50 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/kobweb/compose/ui/modifiers/Layout.kt @@ -0,0 +1,28 @@ +package com.huanshankeji.kobweb.compose.ui.modifiers + +import com.huanshankeji.compose.foundation.layout.ext.cssHeightStretchValueBrowserDependent +import com.huanshankeji.compose.foundation.layout.ext.cssWidthStretchValueBrowserDependent +import com.varabyte.kobweb.compose.css.Height +import com.varabyte.kobweb.compose.css.Width +import com.varabyte.kobweb.compose.css.height +import com.varabyte.kobweb.compose.css.width +import com.varabyte.kobweb.compose.ui.Modifier +import com.varabyte.kobweb.compose.ui.styleModifier +import org.jetbrains.compose.web.css.maxHeight +import org.jetbrains.compose.web.css.maxWidth + + +/** + * A modifier for layouts to make them more consistent with the `androidx.compose` behavior, + * by adding the parent sizes as max constrains for the children. + * Doing this prevents the children to push the parent out of the parent's parent, + * and make the `overflow-*` CSS property / the `*Scroll` modifier work better on JS DOM. + * See https://developer.android.com/develop/ui/compose/layouts/constraints-modifiers for more details. + */ +fun Modifier.imitateAndroidxLayout() = + styleModifier { + width(Width.FitContent) + height(Height.FitContent) + maxWidth(cssWidthStretchValueBrowserDependent) + maxHeight(cssHeightStretchValueBrowserDependent) + } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/kobweb/compose/ui/modifiers/Size.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/kobweb/compose/ui/modifiers/Size.kt index 6514c192..3b05c880 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/kobweb/compose/ui/modifiers/Size.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/kobweb/compose/ui/modifiers/Size.kt @@ -14,5 +14,8 @@ fun Modifier.size(width: Width, height: Height): Modifier = } //TODO consider removing and inlining this +/** + * Consider using [Modifier.imitateAndroidxLayout] instead when adding this for a layout. + */ fun Modifier.sizeFitContent() = size(Width.FitContent, Height.FitContent) diff --git a/compose-multiplatform-lifecycle-viewmodel/build.gradle.kts b/compose-multiplatform-lifecycle-viewmodel/build.gradle.kts new file mode 100644 index 00000000..84648b77 --- /dev/null +++ b/compose-multiplatform-lifecycle-viewmodel/build.gradle.kts @@ -0,0 +1,37 @@ +import com.huanshankeji.team.`Shreck Ye` +import com.huanshankeji.team.pomForTeamDefaultOpenSource + +plugins { + `lib-conventions` +} + +kotlin { + sourceSets { + commonMain { + dependencies { + /* + Use `api`. See: + https://github.com/JetBrains/compose-multiplatform-core/blob/jb-main/lifecycle/lifecycle-viewmodel-compose/build.gradle + https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/lifecycle/lifecycle-viewmodel-compose/build.gradle + */ + api(compose.runtime) + api(commonDependencies.jetbrainsAndroidx.lifecycle.viewmodel()) + } + } + androidxCommonMain { + dependencies { + api(commonDependencies.jetbrainsAndroidx.lifecycle.viewmodelCompose()) + } + } + } +} + +publishing.publications.withType { + pomForTeamDefaultOpenSource( + project, + "Unified Compose Multiplatform ViewModel $FOR_COMPOSE_TARGETS_IN_TITLE", + "Unified wrappers of Compose Multiplatform ViewModel for $FOR_COMPOSE_TARGETS_IN_DESCRIPTION" + ) { + `Shreck Ye`() + } +} diff --git a/compose-multiplatform-lifecycle-viewmodel/src/androidxCommonMain/kotlin/ViewModel.androidxCommon.kt b/compose-multiplatform-lifecycle-viewmodel/src/androidxCommonMain/kotlin/ViewModel.androidxCommon.kt new file mode 100644 index 00000000..955b18e7 --- /dev/null +++ b/compose-multiplatform-lifecycle-viewmodel/src/androidxCommonMain/kotlin/ViewModel.androidxCommon.kt @@ -0,0 +1,10 @@ +package com.huanshankeji.androidx.lifecycle.viewmodel.compose + +import androidx.compose.runtime.Composable +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewmodel.CreationExtras +import androidx.lifecycle.viewmodel.compose.viewModel + +@Composable +actual inline fun viewModel(key: String?, noinline initializer: CreationExtras.() -> VM): VM = + viewModel(key = key, initializer = initializer) diff --git a/compose-multiplatform-lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt b/compose-multiplatform-lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt new file mode 100644 index 00000000..1a6ea197 --- /dev/null +++ b/compose-multiplatform-lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.androidx.lifecycle.viewmodel.compose + +import androidx.compose.runtime.Composable +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewmodel.CreationExtras + +// https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-viewmodel.html + +@Composable +expect inline fun viewModel( + key: String? = null, + noinline initializer: CreationExtras.() -> VM +): VM diff --git a/compose-multiplatform-lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt b/compose-multiplatform-lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt new file mode 100644 index 00000000..ba52ce07 --- /dev/null +++ b/compose-multiplatform-lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt @@ -0,0 +1,10 @@ +package com.huanshankeji.androidx.lifecycle.viewmodel.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewmodel.CreationExtras + +@Composable +actual inline fun viewModel(key: String?, noinline initializer: CreationExtras.() -> VM): VM = + remember(key) { CreationExtras.Empty.initializer() } diff --git a/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/Icons.androidxCommon.kt b/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/Icons.androidxCommon.kt index 5d713f02..41ffd811 100644 --- a/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/Icons.androidxCommon.kt +++ b/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/Icons.androidxCommon.kt @@ -3,3 +3,5 @@ package com.huanshankeji.compose.material.icons import androidx.compose.ui.graphics.vector.ImageVector actual typealias Icon = ImageVector + +//typealias PlatformIcons = androidx.compose.material.icons.Icons // not working because nested objects are not members and can't be referenced with a typealias. diff --git a/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/automirrored/filled/Icons.androidxCommon.kt b/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/automirrored/filled/Icons.androidxCommon.kt new file mode 100644 index 00000000..b6a521b8 --- /dev/null +++ b/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/automirrored/filled/Icons.androidxCommon.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.compose.material.icons.automirrored.filled + +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowForward +import androidx.compose.material.icons.automirrored.filled.Logout +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material.icons.Icons +import androidx.compose.material.icons.Icons as PlatformIcons + +actual val Icons.AutoMirrored.Filled.ArrowBack: Icon get() = PlatformIcons.AutoMirrored.Filled.ArrowBack +actual val Icons.AutoMirrored.Filled.Logout: Icon get() = PlatformIcons.AutoMirrored.Filled.Logout +actual val Icons.AutoMirrored.Filled.ArrowForward: Icon get() = PlatformIcons.AutoMirrored.Filled.ArrowForward diff --git a/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.androidxCommon.kt b/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.androidxCommon.kt index 58e5148e..54c8ed18 100644 --- a/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.androidxCommon.kt +++ b/compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.androidxCommon.kt @@ -1,9 +1,6 @@ package com.huanshankeji.compose.material.icons.filled -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Menu -import androidx.compose.material.icons.filled.Remove -import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.filled.* import com.huanshankeji.compose.material.icons.Icon import com.huanshankeji.compose.material.icons.Icons import androidx.compose.material.icons.Icons as PlatformIcons @@ -12,3 +9,6 @@ actual val Icons.Filled.Add: Icon get() = PlatformIcons.Filled.Add actual val Icons.Filled.Menu: Icon get() = PlatformIcons.Filled.Menu actual val Icons.Filled.Search: Icon get() = PlatformIcons.Filled.Search actual val Icons.Filled.Remove: Icon get() = PlatformIcons.Filled.Remove +actual val Icons.Filled.ArrowDropDown: Icon get() = PlatformIcons.Filled.ArrowDropDown +actual val Icons.Filled.Done: Icon get() = PlatformIcons.Filled.Done +actual val Icons.Filled.Refresh: Icon get() = PlatformIcons.Filled.Refresh diff --git a/compose-multiplatform-material-icons-core/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/automirrored/filled/Icons.kt b/compose-multiplatform-material-icons-core/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/automirrored/filled/Icons.kt new file mode 100644 index 00000000..21340ad8 --- /dev/null +++ b/compose-multiplatform-material-icons-core/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/automirrored/filled/Icons.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.material.icons.automirrored.filled + +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material.icons.Icons + +expect val Icons.AutoMirrored.Filled.ArrowBack: Icon +expect val Icons.AutoMirrored.Filled.Logout: Icon +expect val Icons.AutoMirrored.Filled.ArrowForward: Icon diff --git a/compose-multiplatform-material-icons-core/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.kt b/compose-multiplatform-material-icons-core/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.kt index 7fa162dc..54b004e8 100644 --- a/compose-multiplatform-material-icons-core/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.kt +++ b/compose-multiplatform-material-icons-core/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.kt @@ -7,3 +7,6 @@ expect val Icons.Filled.Add: Icon expect val Icons.Filled.Menu: Icon expect val Icons.Filled.Search: Icon expect val Icons.Filled.Remove: Icon +expect val Icons.Filled.ArrowDropDown: Icon +expect val Icons.Filled.Done: Icon +expect val Icons.Filled.Refresh: Icon diff --git a/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/automirrored/filled/Icons.js.kt b/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/automirrored/filled/Icons.js.kt new file mode 100644 index 00000000..c4884ea2 --- /dev/null +++ b/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/automirrored/filled/Icons.js.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.material.icons.automirrored.filled + +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material.icons.Icons + +actual val Icons.AutoMirrored.Filled.ArrowBack: Icon get() = Icon("arrow_back") +actual val Icons.AutoMirrored.Filled.Logout: Icon get() = Icon("logout") +actual val Icons.AutoMirrored.Filled.ArrowForward: Icon get() = Icon("arrow_forward") diff --git a/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.js.kt b/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.js.kt index c357931b..725c1977 100644 --- a/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.js.kt +++ b/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.js.kt @@ -7,3 +7,6 @@ actual val Icons.Filled.Add: Icon get() = Icon("add") actual val Icons.Filled.Menu: Icon get() = Icon("menu") actual val Icons.Filled.Search: Icon get() = Icon("search") actual val Icons.Filled.Remove: Icon get() = Icon("remove") +actual val Icons.Filled.ArrowDropDown: Icon get() = Icon("arrow_drop_down") +actual val Icons.Filled.Done: Icon get() = Icon("done") +actual val Icons.Filled.Refresh: Icon get() = Icon("refresh") diff --git a/compose-multiplatform-material2/build.gradle.kts b/compose-multiplatform-material2/build.gradle.kts index c2781d50..38bc12eb 100644 --- a/compose-multiplatform-material2/build.gradle.kts +++ b/compose-multiplatform-material2/build.gradle.kts @@ -19,6 +19,7 @@ kotlin { api(project(":compose-multiplatform-common")) api(project(":compose-multiplatform-material-icons-core")) //compileOnly(compose.material) // for KDoc element links only + implementation(commonDependencies.kotlinx.coroutines.core()) } } androidxCommonMain { @@ -43,8 +44,8 @@ kotlin { publishing.publications.withType { pomForTeamDefaultOpenSource( project, - "Compose Multiplatform Material wrappers", - "Material Design component wrappers for Compose Multiplatform (desktop/Android and web)" + "Unified Compose Material 2 wrappers $FOR_COMPOSE_TARGETS_IN_TITLE", + "Unified Material Design 2 component wrappers $FOR_COMPOSE_TARGETS_IN_DESCRIPTION" ) { `Shreck Ye`() } diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Snackbar.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Snackbar.androidxCommon.kt new file mode 100644 index 00000000..31d05627 --- /dev/null +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Snackbar.androidxCommon.kt @@ -0,0 +1,22 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier +import androidx.compose.material.Snackbar as PlatformSnackbar + +@Composable +actual fun Snackbar( + modifier: Modifier, + action: @Composable (() -> Unit)?, + actionOnNewLine: Boolean, + content: @Composable () -> Unit +) = + PlatformSnackbar(modifier.platformModifier, action, actionOnNewLine, content = content) + +@Composable +actual fun Snackbar( + snackbarData: SnackbarData, + modifier: Modifier, + actionOnNewLine: Boolean +) = + PlatformSnackbar(snackbarData.platformValue, modifier.platformModifier, actionOnNewLine) diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/SnackbarHost.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/SnackbarHost.androidxCommon.kt new file mode 100644 index 00000000..a6c7e24e --- /dev/null +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/SnackbarHost.androidxCommon.kt @@ -0,0 +1,75 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.material.Snackbar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier +import androidx.compose.material.SnackbarData as PlatformSnackbarData +import androidx.compose.material.SnackbarDuration as PlatformSnackbarDuration +import androidx.compose.material.SnackbarHostState as PlatformSnackbarHostState +import androidx.compose.material.SnackbarResult as PlatformSnackbarResult + +@Stable +actual class SnackbarHostState(val platformValue: PlatformSnackbarHostState) { + actual constructor() : this(PlatformSnackbarHostState()) + + actual val currentSnackbarData: SnackbarData? + get() = platformValue.currentSnackbarData?.toCommonValue() + + actual suspend fun showSnackbar( + message: String, + actionLabel: String?, + duration: SnackbarDuration + ): SnackbarResult = + platformValue.showSnackbar(message, actionLabel, duration.toPlatformValue()).toCommonValue() +} + +fun PlatformSnackbarHostState.toCommonValue() = + SnackbarHostState(this) + +@Stable +actual class SnackbarData(val platformValue: PlatformSnackbarData) { + actual val message: String + get() = platformValue.message + actual val actionLabel: String? + get() = platformValue.actionLabel + actual val duration: SnackbarDuration + get() = platformValue.duration.toCommonValue() + + actual fun performAction() = platformValue.performAction() + + actual fun dismiss() = platformValue.dismiss() +} + +fun PlatformSnackbarData.toCommonValue() = + SnackbarData(this) + +fun PlatformSnackbarResult.toCommonValue() = + when (this) { + PlatformSnackbarResult.Dismissed -> SnackbarResult.Dismissed + PlatformSnackbarResult.ActionPerformed -> SnackbarResult.ActionPerformed + } + +fun SnackbarDuration.toPlatformValue() = + when (this) { + SnackbarDuration.Short -> PlatformSnackbarDuration.Short + SnackbarDuration.Long -> PlatformSnackbarDuration.Long + SnackbarDuration.Indefinite -> PlatformSnackbarDuration.Indefinite + } + +fun PlatformSnackbarDuration.toCommonValue() = + when (this) { + PlatformSnackbarDuration.Short -> SnackbarDuration.Short + PlatformSnackbarDuration.Long -> SnackbarDuration.Long + PlatformSnackbarDuration.Indefinite -> SnackbarDuration.Indefinite + } + +@Composable +actual fun SnackbarHost( + hostState: SnackbarHostState, + modifier: Modifier, + actionOnNewLine: Boolean +) = + androidx.compose.material.SnackbarHost(hostState.platformValue, modifier.platformModifier) { + Snackbar(it, actionOnNewLine = actionOnNewLine) + } diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Text.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Text.androidxCommon.kt index a5d0c347..a26b2a10 100644 --- a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Text.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Text.androidxCommon.kt @@ -2,7 +2,9 @@ package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.Color +import com.huanshankeji.compose.ui.graphics.toPlatformValue @Composable -actual fun Text(text: String, modifier: Modifier) = - androidx.compose.material.Text(text, modifier.platformModifier) +actual fun Text(text: String, modifier: Modifier, color: Color?) = + androidx.compose.material.Text(text, modifier.platformModifier, color.toPlatformValue()) diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.androidxCommon.kt new file mode 100644 index 00000000..472859e8 --- /dev/null +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.androidxCommon.kt @@ -0,0 +1,15 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material2.Icon +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun IconButton( + onClick: () -> Unit, + modifier: Modifier, + icon: Icon, + contentDescription: String? +) = + com.huanshankeji.compose.material2.IconButton(onClick, modifier) { Icon(icon, contentDescription) } diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.androidxCommon.kt index 514a5ff4..78a65c20 100644 --- a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.androidxCommon.kt @@ -1,5 +1,6 @@ package com.huanshankeji.compose.material2.ext +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup @@ -22,3 +23,7 @@ actual fun RadioRow(selected: Boolean, label: String, onClick: () -> Unit, modif @Composable actual fun RadioGroupRow(modifier: Modifier, content: @Composable () -> Unit) = Row(PlatformModifier.selectableGroup().then(modifier.platformModifier)) { content() } + +@Composable +actual fun RadioGroupColumn(modifier: Modifier, content: @Composable () -> Unit) = + Column(PlatformModifier.selectableGroup().then(modifier.platformModifier)) { content() } \ No newline at end of file diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.androidxCommon.kt index e7c8016b..19715343 100644 --- a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.androidxCommon.kt @@ -4,5 +4,5 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable @Composable -actual fun InlineText(text: String) = +actual fun TaglessText(text: String) = Text(text) diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.androidxCommon.kt index 4f47c58c..afd21087 100644 --- a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.androidxCommon.kt @@ -1,22 +1,26 @@ package com.huanshankeji.compose.material2.ext +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.RowScope import androidx.compose.material.Scaffold import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.PaddingValues +import com.huanshankeji.compose.foundation.layout.toCommonValue import com.huanshankeji.compose.material.icons.Icon import com.huanshankeji.compose.material2.Icon import com.huanshankeji.compose.material2.IconButton import com.huanshankeji.compose.ui.Modifier +import androidx.compose.material.FabPosition.Companion as PlatformFabPosition actual class NavigationIconScope private constructor() { @Composable - actual fun NavButton(onClick: () -> Unit, content: @Composable () -> Unit) = - IconButton(onClick, content = content) + actual fun NavButton(onClick: () -> Unit, modifier: Modifier, content: @Composable () -> Unit) = + IconButton(onClick, modifier, content = content) @Composable - actual fun MaterialIconNavButton(onClick: () -> Unit, icon: Icon, contentDescription: String?) = - IconButton(onClick) { Icon(icon, contentDescription) } + actual fun MaterialIconNavButton(onClick: () -> Unit, modifier: Modifier, icon: Icon, contentDescription: String?) = + IconButton(onClick, modifier) { Icon(icon, contentDescription) } companion object { val instance = NavigationIconScope() @@ -25,27 +29,61 @@ actual class NavigationIconScope private constructor() { actual class TopAppBarActionsScope(val rowScope: RowScope) { @Composable - actual fun ActionButton(onClick: () -> Unit, content: @Composable () -> Unit) = - IconButton(onClick, content = content) + actual fun ActionButton(onClick: () -> Unit, modifier: Modifier, content: @Composable () -> Unit) = + IconButton(onClick, modifier, content = content) @Composable - actual fun MaterialIconActionButton(onClick: () -> Unit, icon: Icon, contentDescription: String?) = - IconButton(onClick) { Icon(icon, contentDescription) } - + actual fun MaterialIconActionButton( + onClick: () -> Unit, modifier: Modifier, icon: Icon, contentDescription: String? + ) = + IconButton(onClick, modifier) { Icon(icon, contentDescription) } } +fun FabPosition.toPlatformValue() = + when (this) { + FabPosition.Start -> PlatformFabPosition.Start + FabPosition.Center -> PlatformFabPosition.Center + FabPosition.End -> PlatformFabPosition.End + } + @Composable -actual fun TopAppBarScaffold( +actual fun PrimitiveTopAppBarScaffold( title: @Composable () -> Unit, topAppBarModifier: Modifier, navigationIcon: @Composable (NavigationIconScope.() -> Unit)?, actions: @Composable TopAppBarActionsScope.() -> Unit, + contentModifier: Modifier, content: @Composable () -> Unit ) = - Scaffold(topBar = { - TopAppBar( - title, - topAppBarModifier.platformModifier, - navigationIcon?.let { { NavigationIconScope.instance.it() } }, - { TopAppBarActionsScope(this).actions() }) - }) { content() } + TopAppBarScaffold(title, topAppBarModifier, navigationIcon, actions) { + Box(contentModifier.platformModifier) { content() } + } + +@Composable +actual fun TopAppBarScaffold( + title: @Composable () -> Unit, + topAppBarModifier: Modifier, + navigationIcon: @Composable (NavigationIconScope.() -> Unit)?, + actions: @Composable TopAppBarActionsScope.() -> Unit, + bottomBar: @Composable (() -> Unit)?, + snackbarHost: @Composable (() -> Unit)?, + floatingActionButton: @Composable (() -> Unit)?, + floatingActionButtonPosition: FabPosition, + isFloatingActionButtonDockedAndroidx: Boolean, + content: @Composable (PaddingValues) -> Unit +) = + Scaffold( + topBar = { + TopAppBar( + title, + topAppBarModifier.platformModifier, + navigationIcon?.let { { NavigationIconScope.instance.it() } }, + { TopAppBarActionsScope(this).actions() }) + }, + bottomBar = bottomBar ?: {}, + snackbarHost = snackbarHost?.let { { snackbarHost() } } + ?: { androidx.compose.material.SnackbarHost(it) }, + floatingActionButton = floatingActionButton ?: {}, + floatingActionButtonPosition = floatingActionButtonPosition.toPlatformValue(), + isFloatingActionButtonDocked = isFloatingActionButtonDockedAndroidx + ) { content(it.toCommonValue()) } diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Snackbar.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Snackbar.kt new file mode 100644 index 00000000..c74d4cda --- /dev/null +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Snackbar.kt @@ -0,0 +1,35 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +// copied and adapted from `Snackbar.kt` in `androidx.compose.material` + +@Composable +expect fun Snackbar( + modifier: Modifier = Modifier, + action: @Composable (() -> Unit)? = null, + actionOnNewLine: Boolean = false, + /* + shape: Shape = MaterialTheme.shapes.small, + backgroundColor: Color = SnackbarDefaults.backgroundColor, + contentColor: Color = MaterialTheme.colors.surface, + elevation: Dp = 6.dp, + */ + content: @Composable () -> Unit +) + +// consider removing `expect` and copying the code from `Snackbar.kt` directly +@Composable +expect fun Snackbar( + snackbarData: SnackbarData, + modifier: Modifier = Modifier, + actionOnNewLine: Boolean = false, + /* + shape: Shape = MaterialTheme.shapes.small, + backgroundColor: Color = SnackbarDefaults.backgroundColor, + contentColor: Color = MaterialTheme.colors.surface, + actionColor: Color = SnackbarDefaults.primaryActionColor, + elevation: Dp = 6.dp + */ +) diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/SnackbarHost.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/SnackbarHost.kt new file mode 100644 index 00000000..65d5aa9e --- /dev/null +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/SnackbarHost.kt @@ -0,0 +1,35 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier + +// Common copies of these 2 types are not used because converting them to and from create additional wrappers and may be expensive. + +@Stable +expect class SnackbarHostState() { + val currentSnackbarData: SnackbarData? + + suspend fun showSnackbar( + message: String, + actionLabel: String? = null, + duration: SnackbarDuration = SnackbarDuration.Short + ): SnackbarResult +} + +@Stable +expect class SnackbarData { + val message: String + val actionLabel: String? + val duration: SnackbarDuration + fun performAction() + fun dismiss() +} + +@Composable +expect fun SnackbarHost( + hostState: SnackbarHostState, + modifier: Modifier = Modifier, + //snackbar: @Composable (SnackbarData) -> Unit = { Snackbar(it) } + actionOnNewLine: Boolean = false // an additional parameter added because passing a custom `Snackbar` comopsable on JS is not supported +) diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/SnackbarHostStateCommonImpl.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/SnackbarHostStateCommonImpl.kt new file mode 100644 index 00000000..671d8917 --- /dev/null +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/SnackbarHostStateCommonImpl.kt @@ -0,0 +1,76 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import kotlinx.coroutines.CancellableContinuation +import kotlinx.coroutines.delay +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.yield +import kotlin.coroutines.resume + +// copied and adapted from `SnackbarHost.kt` in `androidx.compose.material` + +@Stable +abstract class SnackbarHostStateCommonImpl { + private val mutex = Mutex() + var currentSnackbarData by mutableStateOf(null) + private set + + abstract val yieldUntilNext: Boolean + abstract val delayMillisUntilNext: Long + + suspend fun showSnackbar( + message: String, + actionLabel: String? = null, + duration: SnackbarDuration = SnackbarDuration.Short + ): SnackbarResult = mutex.withLock { + try { + return suspendCancellableCoroutine { continuation -> + currentSnackbarData = SnackbarDataImpl(message, actionLabel, duration, continuation) + } + } finally { + currentSnackbarData = null + + // a workaround to trigger recomposition when `currentSnackbarData` is set to `null`, resolving the issue that a series of continuous snackbars don't show + if (yieldUntilNext) yield() + delay(delayMillisUntilNext) + } + } + + @Stable + private class SnackbarDataImpl( + override val message: String, + override val actionLabel: String?, + override val duration: SnackbarDuration, + private val continuation: CancellableContinuation + ) : SnackbarDataCommonInterface { + + override fun performAction() { + if (continuation.isActive) continuation.resume(SnackbarResult.ActionPerformed) + } + + override fun dismiss() { + if (continuation.isActive) continuation.resume(SnackbarResult.Dismissed) + } + } +} + +interface SnackbarDataCommonInterface { + val message: String + val actionLabel: String? + val duration: SnackbarDuration + fun performAction() + fun dismiss() +} + +enum class SnackbarResult { + Dismissed, ActionPerformed +} + +enum class SnackbarDuration { + Short, Long, Indefinite +} diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Text.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Text.kt index 7fc56d92..be4c28ee 100644 --- a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Text.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Text.kt @@ -1,16 +1,17 @@ package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable -import com.huanshankeji.compose.material2.ext.InlineText +import com.huanshankeji.compose.material2.ext.TaglessText import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.Color /** * The `com.huanshankeji.compose.material.Text` function * can be easily confused with other Composable functions named `Text` * such as `androidx.compose.material.Text` and `org.jetbrains.compose.web.dom.Text` * if not careful. - * [InlineText] is recommended over this one when there is no custom [modifier]. - * @see com.huanshankeji.compose.material.ext.MaterialText + * [TaglessText] is recommended over this one when there is no custom [modifier]. + * @see com.huanshankeji.compose.material2.ext.MaterialText */ @Composable -expect fun Text(text: String, modifier: Modifier = Modifier) +expect fun Text(text: String, modifier: Modifier = Modifier, color: Color? = null) diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.kt index 551af428..e117c96e 100644 --- a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.kt @@ -2,14 +2,12 @@ package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable import com.huanshankeji.compose.material.icons.Icon -import com.huanshankeji.compose.material2.Icon import com.huanshankeji.compose.ui.Modifier @Composable -fun IconButton( +expect fun IconButton( onClick: () -> Unit, modifier: Modifier = Modifier, icon: Icon, contentDescription: String? -) = - com.huanshankeji.compose.material2.IconButton(onClick, modifier) { Icon(icon, contentDescription) } +) diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.kt index 2f2b1d47..40944ff0 100644 --- a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.kt @@ -16,4 +16,5 @@ expect fun RadioRow( @Composable expect fun RadioGroupRow(modifier: Modifier = Modifier, content: @Composable () -> Unit) -// TODO `RadioGroupColumn` +@Composable +expect fun RadioGroupColumn(modifier: Modifier = Modifier, content: @Composable () -> Unit) diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.kt index 97589382..579e15d9 100644 --- a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.kt @@ -1,19 +1,33 @@ package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.text.ext.INLINE_TEXT_DEPRECATED_MESSAGE import com.huanshankeji.compose.material2.Text import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.Color /** * An explicit alias of [Text]. */ @Composable -inline fun MaterialText(text: String, modifier: Modifier = Modifier) = - Text(text, modifier) +inline fun MaterialText(text: String, modifier: Modifier = Modifier, color: Color? = null) = + Text(text, modifier, color) + +@Deprecated(INLINE_TEXT_DEPRECATED_MESSAGE, ReplaceWith("TaglessText(text)")) +@Composable +fun InlineText(text: String) = + TaglessText(text) + +/** + * Delegates to raw text without any tag element on JS / Compose HTML. + * @see com.huanshankeji.compose.foundation.text.ext.TaglessBasicText + */ +@Composable +expect fun TaglessText(text: String) /** - * Delegates to raw inline text without any element on JS / Compose HTML. - * @see com.huanshankeji.compose.foundation.text.ext.InlineBasicText + * An alias for [Text]. */ @Composable -expect fun InlineText(text: String) +fun SpanText(text: String, modifier: Modifier = Modifier, color: Color? = null) = + Text(text, modifier, color) diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.kt index 13f9c465..7fbfacad 100644 --- a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.kt @@ -1,30 +1,74 @@ package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.PaddingValues import com.huanshankeji.compose.material.icons.Icon import com.huanshankeji.compose.ui.Modifier expect class NavigationIconScope { @Composable - fun NavButton(onClick: () -> Unit, content: @Composable () -> Unit) + fun NavButton(onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable () -> Unit) @Composable - fun MaterialIconNavButton(onClick: () -> Unit, icon: Icon, contentDescription: String?) + fun MaterialIconNavButton( + onClick: () -> Unit, modifier: Modifier = Modifier, icon: Icon, contentDescription: String? + ) } expect class TopAppBarActionsScope { @Composable - fun ActionButton(onClick: () -> Unit, content: @Composable () -> Unit) + fun ActionButton(onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable () -> Unit) @Composable - fun MaterialIconActionButton(onClick: () -> Unit, icon: Icon, contentDescription: String?) + fun MaterialIconActionButton( + onClick: () -> Unit, modifier: Modifier = Modifier, icon: Icon, contentDescription: String? + ) } +// copied and adapted from `androidx.compose.material.FabPosition` +enum class FabPosition { + Start, Center, End +} + +/** + * This one doesn't fill parent height on JS. + * @param contentModifier be cautious when passing the parameter, as the content CSS class `mdc-top-app-bar--fixed-adjust` has its styles which may be overridden. + */ @Composable -expect fun TopAppBarScaffold( +expect fun PrimitiveTopAppBarScaffold( title: @Composable () -> Unit, topAppBarModifier: Modifier = Modifier, navigationIcon: @Composable (NavigationIconScope.() -> Unit)? = null, actions: @Composable TopAppBarActionsScope.() -> Unit = {}, + contentModifier: Modifier = Modifier, content: @Composable () -> Unit ) + +/** + * This variant fills parent space automatically and internally uses a flexbox on JS. + * For it to work properly on JS DOM, it's recommended to use it as the top level element and to set these CSS styles on body: + * ``` + * body { + * margin: 0; + * height: 100vh; + * } + * ``` + * + * @param snackbarHost `androidx.compose.material.Scaffold` has a `SnackbarHostState` parameter for this lambda, but `androidx.compose.material3.Scaffold` doesn't. Therefore, we think this is probably a design flaw and don't provide the parameter. + * @param isFloatingActionButtonDockedAndroidx available on `androidx.compose` targets only. +// * @param isContentOverflowingOrExpandingJs available on JS DOM only. + */ +@Composable +expect fun TopAppBarScaffold( + title: @Composable () -> Unit, + topAppBarModifier: Modifier = Modifier, + navigationIcon: @Composable (NavigationIconScope.() -> Unit)? = null, + actions: @Composable TopAppBarActionsScope.() -> Unit = {}, + bottomBar: @Composable (() -> Unit)? = null, + snackbarHost: @Composable (() -> Unit)? = null, + floatingActionButton: @Composable (() -> Unit)? = null, + floatingActionButtonPosition: FabPosition = FabPosition.End, + isFloatingActionButtonDockedAndroidx: Boolean = false, + //isContentOverflowingOrExpandingJsDom: Boolean = true, // always overflows + content: @Composable (PaddingValues) -> Unit +) diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.kt index e16d310a..a1369606 100644 --- a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.kt @@ -1,7 +1,7 @@ package com.huanshankeji.compose.material2.lazy.ext import androidx.compose.runtime.Composable -import com.huanshankeji.compose.material2.ext.InlineText +import com.huanshankeji.compose.material2.ext.TaglessText import com.huanshankeji.compose.ui.Modifier expect class ListScope { @@ -34,8 +34,8 @@ class ListItemComponents( val secondaryText: @Composable (() -> Unit)? = null ) { constructor(text: String, secondaryText: String? = null) : this( - { InlineText(text) }, - secondaryText?.let { { InlineText(it) } } + { TaglessText(text) }, + secondaryText?.let { { TaglessText(it) } } ) } diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Card.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Card.js.kt index 60e0a6c5..5f67b633 100644 --- a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Card.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Card.js.kt @@ -3,10 +3,10 @@ package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.PlatformModifier -import com.huanshankeji.kobweb.compose.ui.modifiers.sizeFitContent +import com.huanshankeji.kobweb.compose.ui.modifiers.imitateAndroidxLayout import com.varabyte.kobweb.compose.ui.toAttrs import dev.petuska.kmdc.card.MDCCard @Composable actual fun Card(modifier: Modifier, content: @Composable () -> Unit) = - MDCCard(attrs = PlatformModifier.sizeFitContent().then(modifier.platformModifier).toAttrs()) { content() } + MDCCard(attrs = PlatformModifier.imitateAndroidxLayout().then(modifier.platformModifier).toAttrs()) { content() } diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Snackbar.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Snackbar.js.kt new file mode 100644 index 00000000..8855e18e --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Snackbar.js.kt @@ -0,0 +1,78 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs +import dev.petuska.kmdc.core.MDCAttrs +import dev.petuska.kmdc.snackbar.* + +private fun Boolean.actionOnNewLineToType() = + if (this) MDCSnackbarType.Stacked else MDCSnackbarType.Default + +// see https://github.com/mpetuska/kmdc/blob/master/kmdc/kmdc-snackbar/src/jsMain/kotlin/Actions.kt +@Composable +internal fun CommonSnackbar( + actionOnNewLine: Boolean, + open: Boolean, + timeoutMs: Int?, + modifier: Modifier, + attrs: MDCAttrs?, + mdcSnackbarContent: @Composable MDCSnackbarScope.() -> Unit?, // `MDCContent?` not working here since Kotlin 2.0.0 + actions: @Composable MDCSnackbarActionsScope.() -> Unit?, //MDCContent?, +) = + MDCSnackbar(actionOnNewLine.actionOnNewLineToType(), open, timeoutMs, attrs = modifier.toAttrs(attrs)) { + mdcSnackbarContent.invoke(this) + + actions.let { + Actions { + it() + } + } + } + +@Composable +actual fun Snackbar( + modifier: Modifier, + action: @Composable (() -> Unit)?, + actionOnNewLine: Boolean, + content: @Composable () -> Unit +) = + CommonSnackbar(actionOnNewLine, true, null, modifier, null, { + Label { content() } + }, { + action?.let { Action { it() } } + }) + +/** + * @param snackbarData [SnackbarDataCommonInterface.duration] is not used here. Use [timeoutMs] instead. + */ +@Composable +internal fun CommonSnackbar( + actionOnNewLine: Boolean, + open: Boolean, + timeoutMs: Int?, + modifier: Modifier, + snackbarData: SnackbarDataCommonInterface? +) = + CommonSnackbar(actionOnNewLine, open, timeoutMs, modifier, { + snackbarData?.let { snackbarData -> onClosed { snackbarData.dismiss() } } // This event is not fired when the snackbar UI is not opened. + }, { + Label(snackbarData?.message ?: "") // to reduce DOM recomposition in `SnackbarHost` + }, { + snackbarData?.actionLabel?.let { + Action(it, attrs = { + onClick { snackbarData.performAction() } + }) + } + }) + +/** + * @param snackbarData [SnackbarDataCommonInterface.duration] is not used here. + */ +@Composable +actual fun Snackbar( + snackbarData: SnackbarData, + modifier: Modifier, + actionOnNewLine: Boolean +) = + CommonSnackbar(actionOnNewLine, true, null, modifier, snackbarData.platformValue) diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/SnackbarHost.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/SnackbarHost.js.kt new file mode 100644 index 00000000..0b60347a --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/SnackbarHost.js.kt @@ -0,0 +1,93 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier +import dev.petuska.kmdc.snackbar.MDCSnackbarAttrsScope +import kotlinx.coroutines.delay + +// copied and adapted from `SnackbarHost.kt` in `androidx.compose.material` + +@Stable +actual class SnackbarHostState(val platformValue: SnackbarHostStateCommonImpl) { + actual constructor() : this(object : SnackbarHostStateCommonImpl() { + override val yieldUntilNext: Boolean + get() = false + override val delayMillisUntilNext: Long + get() = 20 // for 60 Hz displays + }) + + actual val currentSnackbarData: SnackbarData? + get() = platformValue.currentSnackbarData?.toCommonValue() + + actual suspend fun showSnackbar( + message: String, + actionLabel: String?, + duration: SnackbarDuration + ): SnackbarResult = + platformValue.showSnackbar(message, actionLabel, duration) +} + +@Stable +actual class SnackbarData(val platformValue: SnackbarDataCommonInterface) { + actual val message: String + get() = platformValue.message + actual val actionLabel: String? + get() = platformValue.actionLabel + actual val duration: SnackbarDuration + get() = platformValue.duration + + actual fun performAction() = platformValue.performAction() + + actual fun dismiss() = platformValue.dismiss() +} + +fun SnackbarDataCommonInterface.toCommonValue() = + SnackbarData(this) + +@Composable +actual fun SnackbarHost( + hostState: SnackbarHostState, + modifier: Modifier, + actionOnNewLine: Boolean +) { + val currentSnackbarData = hostState.currentSnackbarData?.platformValue + + val duration: Long? + if (currentSnackbarData !== null) { + duration = currentSnackbarData.duration.toMillis() + + /** + * See commit a1dfeaea82384b6e33b3807482ed01ba4311075f for some alternative approaches + * to solve the issue that a series of continuous snackbars don't work on JS without [SnackbarHostStateCommonImpl.delayMillisUntilNext], + * which all failed. + */ + /** + * It seems possible that the [SnackbarHostStateCommonImpl.delayMillisUntilNext] in [SnackbarHostState] is not enough to trigger recomposition, + * the snackbar is updated with the next [SnackbarData], + * the [MDCSnackbarAttrsScope.onClosed] in [CommonSnackbar] is never called, + * and [SnackbarHostStateCommonImpl.mutex] never gets unlocked. + * This [LaunchedEffect] makes sure it's unlocked, though some queuing snackbars may not show when this happens. + */ + // also listen for the close event. + LaunchedEffect(currentSnackbarData) { + delay(duration) + currentSnackbarData.dismiss() + } + } else + duration = null + + // not put in a conditional block to reduce DOM recomposition + CommonSnackbar(actionOnNewLine, currentSnackbarData !== null, duration?.toInt(), modifier, currentSnackbarData) +} + +internal fun SnackbarDuration.toMillis( + //hasAction: Boolean, + //accessibilityManager: AccessibilityManager? +): Long = + when (this) { + SnackbarDuration.Indefinite -> Long.MAX_VALUE + SnackbarDuration.Long -> 10000L + SnackbarDuration.Short -> 4000L + } diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Text.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Text.js.kt index a3733ad5..20c53f34 100644 --- a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Text.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Text.js.kt @@ -1,9 +1,12 @@ package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable -import com.huanshankeji.compose.foundation.text.BasicText +import androidx.compose.runtime.InternalComposeApi +import com.huanshankeji.compose.foundation.text.ext.CommonBasicText import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.Color @Composable -actual fun Text(text: String, modifier: Modifier) = - BasicText(text, modifier) +actual fun Text(text: String, modifier: Modifier, color: Color?) = + @OptIn(InternalComposeApi::class) + CommonBasicText(text, modifier, color) diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.js.kt new file mode 100644 index 00000000..fb2ba9c2 --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.js.kt @@ -0,0 +1,24 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material2.icons.mdcIconWithStyle +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs +import dev.petuska.kmdc.icon.button.MDCIconButton +import org.jetbrains.compose.web.dom.Text + +@Composable +actual fun IconButton( + onClick: () -> Unit, + modifier: Modifier, + icon: Icon, + contentDescription: String? +) { + MDCIconButton(attrs = modifier.toAttrs { + mdcIconWithStyle() + onClick { onClick() } + }) { + Text(icon.name) + } +} diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.js.kt index 8dd668f7..4885a786 100644 --- a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.js.kt @@ -5,17 +5,36 @@ import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.toAttrs import dev.petuska.kmdc.form.field.MDCFormField import dev.petuska.kmdc.radio.MDCRadio +import org.jetbrains.compose.web.css.* +import org.jetbrains.compose.web.dom.Div // see: https://github.com/mpetuska/kmdc/blob/master/sandbox/src/jsMain/showcases/MDCRadio.kt @Composable actual fun RadioRow(selected: Boolean, label: String, onClick: () -> Unit, modifier: Modifier, enabled: Boolean) = - MDCRadio(selected, disabled = !enabled, label = label, attrs = modifier.toAttrs { - onClick { onClick() } - }) + Div { + // `MDCRadio` adds 2 elements, which can cause the radio button and the label to not be on a same row if not wrapped in a `div`. + MDCRadio(selected, disabled = !enabled, label = label, attrs = modifier.toAttrs { + onClick { onClick() } + }) + } @Composable actual fun RadioGroupRow(modifier: Modifier, content: @Composable () -> Unit) = MDCFormField(attrs = modifier.toAttrs()) { content() } + +@Composable +actual fun RadioGroupColumn(modifier: Modifier, content: @Composable () -> Unit) = + MDCFormField(attrs = modifier.toAttrs { + style { + display(DisplayStyle.Flex) + flexDirection(FlexDirection.Column) + alignItems(AlignItems.Start) + } + }) { + //Column { + content() + //} + } diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Text.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Text.js.kt index 85e471c1..5bd07abe 100644 --- a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Text.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Text.js.kt @@ -4,5 +4,5 @@ import androidx.compose.runtime.Composable import org.jetbrains.compose.web.dom.Text @Composable -actual fun InlineText(text: String) = +actual fun TaglessText(text: String) = Text(text) diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.js.kt index 57792107..59245bd7 100644 --- a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.js.kt @@ -5,6 +5,7 @@ import com.huanshankeji.compose.foundation.text.KeyboardActions import com.huanshankeji.compose.foundation.text.KeyboardOptions import com.huanshankeji.compose.foundation.text.attrsFrom import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material2.icons.mdcIconWithStyle import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.toAttrs import dev.petuska.kmdc.core.MDCContent @@ -14,7 +15,6 @@ import dev.petuska.kmdc.textfield.MDCTextFieldScope import dev.petuska.kmdc.textfield.MDCTextFieldType import dev.petuska.kmdc.textfield.icon.MDCTextFieldLeadingIcon import dev.petuska.kmdc.textfield.icon.MDCTextFieldTrailingIcon -import dev.petuska.kmdcx.icons.mdcIcon import org.jetbrains.compose.web.dom.Text @Composable @@ -91,8 +91,8 @@ fun CommonTextFieldWithMaterialIcons( enabled, label, // see https://github.com/mpetuska/kmdc/blob/master/sandbox/src/jsMain/showcases/MDCTextField.kt - leadingIcon?.let { { MDCTextFieldLeadingIcon(attrs = { mdcIcon() }) { Text(it.name) } } }, - trailingIcon?.let { { MDCTextFieldTrailingIcon(attrs = { mdcIcon() }) { Text(it.name) } } }, + leadingIcon?.let { { MDCTextFieldLeadingIcon(attrs = { mdcIconWithStyle() }) { Text(it.name) } } }, + trailingIcon?.let { { MDCTextFieldTrailingIcon(attrs = { mdcIconWithStyle() }) { Text(it.name) } } }, keyboardOptions, keyboardActions ) diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.js.kt index d3befae4..6e326d04 100644 --- a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.js.kt @@ -2,45 +2,59 @@ package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable import com.huanshankeji.compose.contentDescription +import com.huanshankeji.compose.foundation.ext.matchPositionRelativeParent +import com.huanshankeji.compose.foundation.layout.Column +import com.huanshankeji.compose.foundation.layout.PaddingValues +import com.huanshankeji.compose.foundation.layout.ext.fillMaxWidthStretch +import com.huanshankeji.compose.foundation.layout.fillMaxSize import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material2.icons.mdcIconWithStyle import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.toAttrs +import com.varabyte.kobweb.compose.css.TextAlign +import com.varabyte.kobweb.compose.css.textAlign import dev.petuska.kmdc.top.app.bar.* -import dev.petuska.kmdcx.icons.mdcIcon +import org.jetbrains.compose.web.css.* +import org.jetbrains.compose.web.dom.Div import org.jetbrains.compose.web.dom.Text actual class NavigationIconScope(val mdcTopAppBarSectionScope: MDCTopAppBarSectionScope) { @Composable - actual fun NavButton(onClick: () -> Unit, content: @Composable () -> Unit) = - mdcTopAppBarSectionScope.NavButton(attrs = { onClick { onClick() } }) { content() } + actual fun NavButton(onClick: () -> Unit, modifier: Modifier, content: @Composable () -> Unit) = + mdcTopAppBarSectionScope.NavButton(attrs = modifier.toAttrs { onClick { onClick() } }) { content() } @Composable - actual fun MaterialIconNavButton(onClick: () -> Unit, icon: Icon, contentDescription: String?) = - mdcTopAppBarSectionScope.NavButton(attrs = { - mdcIcon() + actual fun MaterialIconNavButton(onClick: () -> Unit, modifier: Modifier, icon: Icon, contentDescription: String?) = + mdcTopAppBarSectionScope.NavButton(attrs = modifier.toAttrs { + onClick { onClick() } + mdcIconWithStyle() contentDescription(contentDescription) }) { Text(icon.name) } } actual class TopAppBarActionsScope(val mdcTopAppBarSectionScope: MDCTopAppBarSectionScope) { @Composable - actual fun ActionButton(onClick: () -> Unit, content: @Composable () -> Unit) = - mdcTopAppBarSectionScope.ActionButton(attrs = { onClick { onClick() } }) { content() } + actual fun ActionButton(onClick: () -> Unit, modifier: Modifier, content: @Composable () -> Unit) = + mdcTopAppBarSectionScope.ActionButton(attrs = modifier.toAttrs { onClick { onClick() } }) { content() } @Composable - actual fun MaterialIconActionButton(onClick: () -> Unit, icon: Icon, contentDescription: String?) = - mdcTopAppBarSectionScope.ActionButton(attrs = { - mdcIcon() + actual fun MaterialIconActionButton( + onClick: () -> Unit, modifier: Modifier, icon: Icon, contentDescription: String? + ) = + mdcTopAppBarSectionScope.ActionButton(attrs = modifier.toAttrs { + onClick { onClick() } + mdcIconWithStyle() contentDescription(contentDescription) }) { Text(icon.name) } } @Composable -actual fun TopAppBarScaffold( +actual fun PrimitiveTopAppBarScaffold( title: @Composable () -> Unit, topAppBarModifier: Modifier, navigationIcon: @Composable (NavigationIconScope.() -> Unit)?, actions: @Composable TopAppBarActionsScope.() -> Unit, + contentModifier: Modifier, content: @Composable () -> Unit ) = MDCTopAppBar { @@ -60,5 +74,76 @@ actual fun TopAppBarScaffold( } } } - Main { content() } + Main(contentModifier.toAttrs()) { content() } + } + +/** + * It's highly recommended to read the KDoc in the common module. + */ +@Composable +actual fun TopAppBarScaffold( + title: @Composable () -> Unit, + topAppBarModifier: Modifier, + navigationIcon: @Composable (NavigationIconScope.() -> Unit)?, + actions: @Composable TopAppBarActionsScope.() -> Unit, + bottomBar: @Composable (() -> Unit)?, + snackbarHost: @Composable (() -> Unit)?, + floatingActionButton: @Composable (() -> Unit)?, + floatingActionButtonPosition: FabPosition, + isFloatingActionButtonDockedAndroidx: Boolean, + content: @Composable (PaddingValues) -> Unit +) { + @Composable + fun fabWithPosition(floatingActionButton: @Composable (() -> Unit)) = + Div({ + style { + position(Position.Absolute) + bottom(16.px) + when (floatingActionButtonPosition) { + FabPosition.Start -> left(16.px) + FabPosition.Center -> { + width(100.percent) + textAlign(TextAlign.Center) + } + + FabPosition.End -> right(16.px) + } + } + }) { + floatingActionButton() + } + + Column(Modifier.fillMaxSize()) { + PrimitiveTopAppBarScaffold( + title, + topAppBarModifier, + navigationIcon, + actions, + Modifier.weight(1f).fillMaxWidthStretch() + ) { + // The content gets hidden behind the top app bar if this div is not added. + Div({ + style { + height(100.percent) + position(Position.Relative) // same issue as above if not added + } + }) { + Div({ + style { + matchPositionRelativeParent() + //overflow(Overflow.Auto) // This seems not needed. TODO remove if confirmed to be not needed + } + }) { + // see `ScaffoldLayoutWithMeasureFix` + val innerPadding = PaddingValues() + content(innerPadding) + } + + floatingActionButton?.let { fabWithPosition(it) } + } + } + + bottomBar?.invoke() } + snackbarHost?.let { it() } +} diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/icons/IconsMdc.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/icons/IconsMdc.kt index bd2a5b71..5a2d3d25 100644 --- a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/icons/IconsMdc.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/icons/IconsMdc.kt @@ -2,8 +2,24 @@ package com.huanshankeji.compose.material2.icons import com.huanshankeji.compose.material.icons.Icon import dev.petuska.kmdcx.icons.MDCIcon +import dev.petuska.kmdcx.icons.MDCIconType +import dev.petuska.kmdcx.icons.mdcIcon +import org.jetbrains.compose.web.attributes.AttrsScope val mdcIconMap = MDCIcon.entries.associateBy { it.type } fun Icon.toMDCIcon() = mdcIconMap.getValue(name) + + +// Copied and adapted from https://github.com/mpetuska/kmdc/blob/fe1c35086e05d66b66c1deff5dac23dc4dcffbcd/kmdcx/kmdcx-icons/src/jsMain/kotlin/MDCIcon.kt#L16-L17 +// for places where this style should be but is not imported +@JsNonModule +@JsModule("material-icons/iconfont/material-icons.css") +internal external val Style: dynamic + +//@MDCAttrsDsl +fun AttrsScope<*>.mdcIconWithStyle(type: MDCIconType = MDCIconType.Filled) { + Style + mdcIcon(type) +} diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.js.kt index 522c9da3..14229dd7 100644 --- a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.js.kt @@ -1,7 +1,7 @@ package com.huanshankeji.compose.material2.lazy.ext import androidx.compose.runtime.Composable -import com.huanshankeji.compose.foundation.verticalScrollPlatformModifier +import com.huanshankeji.compose.foundation.imitateAndroidxLayoutVerticalScrollPlatformModifier import com.huanshankeji.compose.runtime.DeferredComposableRunner import com.huanshankeji.compose.ui.Modifier import com.varabyte.kobweb.compose.ui.toAttrs @@ -78,6 +78,6 @@ actual class HeaderScope(val headingElementScope: ElementScope Unit) = - MDCList(attrs = verticalScrollPlatformModifier.then(modifier.platformModifier).toAttrs()) { + MDCList(attrs = imitateAndroidxLayoutVerticalScrollPlatformModifier.then(modifier.platformModifier).toAttrs()) { ListScope(this).ComposableRun(content) } diff --git a/compose-multiplatform-material3/build.gradle.kts b/compose-multiplatform-material3/build.gradle.kts index 9ae453fa..344bbd20 100644 --- a/compose-multiplatform-material3/build.gradle.kts +++ b/compose-multiplatform-material3/build.gradle.kts @@ -1,11 +1,19 @@ + import com.huanshankeji.team.`Shreck Ye` import com.huanshankeji.team.pomForTeamDefaultOpenSource plugins { `lib-conventions` + //id("com.android.library") } kotlin { + /* + androidTarget { + publishLibraryVariants("release", "debug") + } + */ + sourceSets { /* Use `api`. See: @@ -37,8 +45,8 @@ kotlin { publishing.publications.withType { pomForTeamDefaultOpenSource( project, - "Compose Multiplatform Material 3 wrappers", - "Compose Multiplatform Material Design 3 component wrappers for `androidx.compose` and Compose HTML" + "Unified Compose Material 3 wrappers $FOR_COMPOSE_TARGETS_IN_TITLE", + "Unified Material Design 3 component wrappers $FOR_COMPOSE_TARGETS_IN_DESCRIPTION" ) { `Shreck Ye`() } diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ProgressIndicator.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ProgressIndicator.androidxCommon.kt new file mode 100644 index 00000000..55914fc4 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ProgressIndicator.androidxCommon.kt @@ -0,0 +1,22 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier +import androidx.compose.material3.CircularProgressIndicator as PlatformCircularProgressIndicator +import androidx.compose.material3.LinearProgressIndicator as PlatformLinearProgressIndicator + +@Composable +actual fun LinearProgressIndicator(progress: () -> Float, modifier: Modifier) = + PlatformLinearProgressIndicator(progress, modifier.platformModifier) + +@Composable +actual fun LinearProgressIndicator(modifier: Modifier) = + PlatformLinearProgressIndicator(modifier.platformModifier) + +@Composable +actual fun CircularProgressIndicator(progress: () -> Float, modifier: Modifier) = + PlatformCircularProgressIndicator(progress, modifier.platformModifier) + +@Composable +actual fun CircularProgressIndicator(modifier: Modifier) = + PlatformCircularProgressIndicator(modifier.platformModifier) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Text.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Text.androidxCommon.kt index 2bbe2f8e..702b0486 100644 --- a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Text.androidxCommon.kt +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Text.androidxCommon.kt @@ -2,7 +2,9 @@ package com.huanshankeji.compose.material3 import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.Color +import com.huanshankeji.compose.ui.graphics.toPlatformValue @Composable -actual fun Text(text: String, modifier: Modifier) = - androidx.compose.material3.Text(text, modifier.platformModifier) +actual fun Text(text: String, modifier: Modifier, color: Color?) = + androidx.compose.material3.Text(text, modifier.platformModifier, color.toPlatformValue()) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Button.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Button.androidxCommon.kt index 3e86a435..aa8922e8 100644 --- a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Button.androidxCommon.kt +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Button.androidxCommon.kt @@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp -import com.huanshankeji.compose.layout.size +import com.huanshankeji.compose.foundation.layout.size import com.huanshankeji.compose.ui.Modifier @Composable diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/ExposedDropdownMenu.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/ExposedDropdownMenu.androidxCommon.kt new file mode 100644 index 00000000..017e9434 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/ExposedDropdownMenu.androidxCommon.kt @@ -0,0 +1,62 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuDefaults +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.PlatformModifier + +@Composable +actual fun ExposedDropdownMenuBox( + expanded: Boolean, + onExpandedChange: (Boolean) -> Unit, + modifier: Modifier, + content: @Composable ExposedDropdownMenuBoxScope.() -> Unit +) = + @OptIn(ExperimentalMaterial3Api::class) + androidx.compose.material3.ExposedDropdownMenuBox(expanded, onExpandedChange, modifier.platformModifier) { + ExposedDropdownMenuBoxScope(this).content() + } + +@OptIn(ExperimentalMaterial3Api::class) +actual class ExposedDropdownMenuBoxScope(val platformValue: androidx.compose.material3.ExposedDropdownMenuBoxScope) { + actual fun Modifier.menuAnchor(): Modifier = + platformModify { with(platformValue) { menuAnchor() } } + + @Composable + actual fun ExposedDropdownMenu( + expanded: Boolean, + onDismissRequestAndroidx: () -> Unit, + onCloseJsDom: () -> Unit, + modifier: Modifier, + content: @Composable () -> Unit + ) = + platformValue.ExposedDropdownMenu( + expanded, + onDismissRequestAndroidx, + modifier.platformModifier + ) { content() } +} + + +@Composable +actual fun ExposedDropdownMenuBoxScope.ExposedDropdownMenuBoxTextField( + expanded: Boolean, args: ExposedDropdownMenuBoxTextFieldArgs +) = + with(args) { + @OptIn(ExperimentalMaterial3Api::class) + TextField( + modifier = with(platformValue) { PlatformModifier.menuAnchor(/*MenuAnchorType.PrimaryNotEditable*/) }, // `MenuAnchorType` seems to be not supported in the latest version of Compose Multiplatform yet + value = value, + onValueChange = onValueChange, + readOnly = readOnly, + singleLine = singleLine, + label = { Text(label) }, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, + colors = ExposedDropdownMenuDefaults.textFieldColors(), + ) + } + +//internal expect fun menuAnchorModifier(editable: Boolean) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Menu.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Menu.androidxCommon.kt new file mode 100644 index 00000000..6c5f23e2 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Menu.androidxCommon.kt @@ -0,0 +1,70 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.DpOffset +import com.huanshankeji.compose.toContentWithoutModifier +import com.huanshankeji.compose.toNullableContentWithoutModifier +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun DropdownMenu( + expanded: Boolean, + onDismissRequestAndroidx: () -> Unit, + onCloseJsDom: () -> Unit, + modifier: Modifier, + offset: DpOffset, + content: @Composable () -> Unit +) = + androidx.compose.material3.DropdownMenu( + expanded, + onDismissRequestAndroidx, + modifier.platformModifier, + offset + ) { + content() + } + +actual class DropdownMenuBoxScope { + actual fun Modifier.menuAnchorJsDom(): Modifier = + this // do nothing + + @Composable + actual fun DropdownMenu( + expanded: Boolean, + onDismissRequestAndroidx: () -> Unit, + onCloseJsDom: () -> Unit, + modifier: Modifier, + offset: DpOffset, + content: @Composable () -> Unit + ) = + androidx.compose.material3.DropdownMenu( + expanded, onDismissRequestAndroidx, modifier.platformModifier, offset + ) { content() } +} + +@Composable +actual fun DropdownMenuBox(content: @Composable DropdownMenuBoxScope.() -> Unit) = + // There is currently no extra `Box` wrapping the content yet. Consider adding one. See https://stackoverflow.com/a/68728525/5082913. + DropdownMenuBoxScope().content() + +/** + * @param onClick you are supposed to set the `expanded` state of the parent [DropdownMenu] or [ExposedDropdownMenuBoxScope.ExposedDropdownMenu] to `false` in this callback to ensure consistency on JS. + */ +@Composable +actual fun DropdownMenuItem( + text: @Composable (Modifier) -> Unit, + onClick: () -> Unit, + modifier: Modifier, + leadingIcon: @Composable ((Modifier) -> Unit)?, + trailingIcon: @Composable ((Modifier) -> Unit)?, + enabled: Boolean, + keepOpenJsDom: Boolean +) = + androidx.compose.material3.DropdownMenuItem( + text.toContentWithoutModifier(), + onClick, + modifier.platformModifier, + leadingIcon.toNullableContentWithoutModifier(), + trailingIcon.toNullableContentWithoutModifier(), + enabled + ) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.androidxCommon.kt index 995b5c54..e9471e2e 100644 --- a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.androidxCommon.kt +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.androidxCommon.kt @@ -34,6 +34,6 @@ actual fun NavigationBarScope.NavigationBarItem( (if (selected) selectedIcon else unselectedIcon).toContentWithoutModifier(), modifier.platformModifier, enabled, - label.toNullableInlineText(), + label.toNullableTaglessText(), alwaysShowLabel ) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Text.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Text.androidxCommon.kt index 4fcc7557..bf8345c0 100644 --- a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Text.androidxCommon.kt +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Text.androidxCommon.kt @@ -4,5 +4,5 @@ import androidx.compose.runtime.Composable import com.huanshankeji.compose.material3.Text @Composable -actual fun InlineText(text: String) = +actual fun TaglessText(text: String) = Text(text) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ProgressIndicator.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ProgressIndicator.kt new file mode 100644 index 00000000..0d7a1e25 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ProgressIndicator.kt @@ -0,0 +1,38 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +/** + * Determinate + */ +@Composable +expect fun LinearProgressIndicator( + progress: () -> Float, + modifier: Modifier = Modifier +) + +/** + * Indeterminate + */ +@Composable +expect fun LinearProgressIndicator( + modifier: Modifier = Modifier +) + +/** + * Determinate + */ +@Composable +expect fun CircularProgressIndicator( + progress: () -> Float, + modifier: Modifier = Modifier +) + +/** + * Indeterminate + */ +@Composable +expect fun CircularProgressIndicator( + modifier: Modifier = Modifier +) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/SnackbarHost.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/SnackbarHost.kt new file mode 100644 index 00000000..37963460 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/SnackbarHost.kt @@ -0,0 +1,51 @@ +package com.huanshankeji.compose.material3 + +/* +// copied and adapted from `SnackbarHost.kt` in `androidx.compose.material3` + +@Stable +expect class SnackbarHostState { + var currentSnackbarData by mutableStateOf(null) + private set + + suspend fun showSnackbar( + message: String, + actionLabel: String? = null, + withDismissAction: Boolean = false, + duration: SnackbarDuration = + if (actionLabel == null) SnackbarDuration.Short else SnackbarDuration.Indefinite + ): SnackbarResult + + suspend fun showSnackbar(visuals: SnackbarVisuals): SnackbarResult +} + +@Composable +expect fun SnackbarHost( + hostState: SnackbarHostState, + modifier: Modifier = Modifier, + snackbar: @Composable (SnackbarData) -> Unit = { Snackbar(it) } +) + +@Stable +interface SnackbarVisuals { + val message: String + val actionLabel: String? + val withDismissAction: Boolean + val duration: SnackbarDuration +} + +@Stable +interface SnackbarData { + val visuals: SnackbarVisuals + fun performAction() + fun dismiss() +} + +enum class SnackbarResult { + Dismissed, ActionPerformed +} + +enum class SnackbarDuration { + Short, Long, Indefinite +} +*/ diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Text.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Text.kt index 3ae5a7de..ada25918 100644 --- a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Text.kt +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Text.kt @@ -1,16 +1,17 @@ package com.huanshankeji.compose.material3 import androidx.compose.runtime.Composable -import com.huanshankeji.compose.material3.ext.InlineText +import com.huanshankeji.compose.material3.ext.TaglessText import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.Color /** * The `com.huanshankeji.compose.material.Text` function * can be easily confused with other Composable functions named `Text` * such as `androidx.compose.material.Text` and `org.jetbrains.compose.web.dom.Text` * if not careful. - * [InlineText] is recommended over this one when there is no custom [modifier]. + * [TaglessText] is recommended over this one when there is no custom [modifier]. * @see com.huanshankeji.compose.material3.ext.MaterialText */ @Composable -expect fun Text(text: String, modifier: Modifier = Modifier) +expect fun Text(text: String, modifier: Modifier = Modifier, color: Color? = null) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/ExposedDropdownMenu.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/ExposedDropdownMenu.kt new file mode 100644 index 00000000..b370a4d5 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/ExposedDropdownMenu.kt @@ -0,0 +1,85 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +// still in the `ext` package because `content` doesn't take a `ColumnScope` receiver + +// copied and adapted from `ExposedDropdownMenu.kt` in `androidx.compose.material3` + +@Composable +expect fun ExposedDropdownMenuBox( + expanded: Boolean, + onExpandedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + content: @Composable ExposedDropdownMenuBoxScope.() -> Unit +) + +expect class ExposedDropdownMenuBoxScope { + fun Modifier.menuAnchor(): Modifier + + /* + abstract fun Modifier.exposedDropdownSize( + matchTextFieldWidth: Boolean = true + ): Modifier + */ + + /** + * @param onDismissRequestAndroidx not supported on JS. + * @param onCloseJsDom JS only. + */ + @Composable + fun ExposedDropdownMenu( + expanded: Boolean, + onDismissRequestAndroidx: () -> Unit, + onCloseJsDom: () -> Unit, + modifier: Modifier = Modifier, + //scrollState: ScrollState = rememberScrollState(), + content: @Composable /*ColumnScope.*/() -> Unit + ) + + //TODO for `DropdownMenuItem`: `contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding` +} + + +@Composable +fun ExposedDropdownMenuBoxWithTextField( + expanded: Boolean, + onExpandedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + textFieldArgs: ExposedDropdownMenuBoxTextFieldArgs, + //scrollState: ScrollState = rememberScrollState(), + exposedDropdownMenuArgs: ExposedDropdownMenuArgs +) = + // adapted from the examples in https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#ExposedDropdownMenuBox(kotlin.Boolean,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1) + ExposedDropdownMenuBox(expanded, onExpandedChange, modifier) { + ExposedDropdownMenuBoxTextField(expanded, textFieldArgs) + with(exposedDropdownMenuArgs) { + ExposedDropdownMenu( + this.expanded, onDismissRequestAndroidx, onCloseJsDom, this.modifier, content + ) + } + } + +class ExposedDropdownMenuBoxTextFieldArgs( + val value: String, + val onValueChange: (String) -> Unit = {}, // pass this only when specifying custom values in the text field + val readOnly: Boolean = true, + val singleLine: Boolean = true, + val label: String +) + +@Composable +expect fun ExposedDropdownMenuBoxScope.ExposedDropdownMenuBoxTextField( + expanded: Boolean, + args: ExposedDropdownMenuBoxTextFieldArgs +) +// TODO Set `menuAnchor` on the icon too when the text field is editable and `MenuAnchorType.SecondaryEditable` is supported. See https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#ExposedDropdownMenuBox(kotlin.Boolean,kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1). + +class ExposedDropdownMenuArgs( + val expanded: Boolean, + val onDismissRequestAndroidx: () -> Unit, + val onCloseJsDom: () -> Unit, + val modifier: Modifier = Modifier, + val content: @Composable /*ColumnScope.*/() -> Unit +) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Menu.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Menu.kt new file mode 100644 index 00000000..dc05ffd7 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Menu.kt @@ -0,0 +1,79 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.dp +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.ui.Modifier + +internal val defaultDpOffset = DpOffset(0.dp, 0.dp) + +/** + * @see DropdownMenuBoxScope.DropdownMenu + */ +@Deprecated("This implementation doesn't have the `anchorElement` set on JS DOM and thus doesn't work directly. Use `DropdownMenuBox`, `DropdownMenuBoxScope.menuAnchorJs`, and `DropdownMenuBoxScope.DropdownMenu` instead.") +@Composable +expect fun DropdownMenu( + expanded: Boolean, + onDismissRequestAndroidx: () -> Unit, + onCloseJsDom: () -> Unit, + modifier: Modifier = Modifier, + offset: DpOffset = defaultDpOffset, + //scrollState: ScrollState = rememberScrollState(), + content: @Composable () -> Unit +) + +expect class DropdownMenuBoxScope { + fun Modifier.menuAnchorJsDom(): Modifier + + /** + * @param onDismissRequestAndroidx not supported on JS. + * @param onCloseJsDom JS only + * @see com.huanshankeji.compose.material3.ext.DropdownMenu + */ + @Composable + fun DropdownMenu( + expanded: Boolean, + onDismissRequestAndroidx: () -> Unit, + onCloseJsDom: () -> Unit, + modifier: Modifier = Modifier, + offset: DpOffset = defaultDpOffset, + //scrollState: ScrollState = rememberScrollState(), + content: @Composable () -> Unit + ) +} + +@Composable +expect fun DropdownMenuBox(content: @Composable DropdownMenuBoxScope.() -> Unit) + +/** + * @param keepOpenJsDom set to `true` for completely consistent behavior on JS to `androidx.compose`. However, if you set the `expanded` state to false in [onClick], doing this is unnecessary. + */ +@Composable +expect fun DropdownMenuItem( + text: @Composable (Modifier) -> Unit, + onClick: () -> Unit, + modifier: Modifier = Modifier, + leadingIcon: @Composable ((Modifier) -> Unit)? = null, + trailingIcon: @Composable ((Modifier) -> Unit)? = null, + enabled: Boolean = true, + keepOpenJsDom: Boolean = false +) + +@Composable +fun DropdownMenuItemWithMaterialIcons( + text: @Composable (Modifier) -> Unit, + onClick: () -> Unit, + modifier: Modifier = Modifier, + leadingIcon: Icon? = null, + trailingIcon: Icon? = null, + enabled: Boolean = true +) = + DropdownMenuItem( + text, + onClick, + modifier, + leadingIcon.toNullableContentWithModifier(), + trailingIcon.toNullableContentWithModifier(), + enabled + ) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Text.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Text.kt index 4d52d20a..0ef1e1bf 100644 --- a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Text.kt +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Text.kt @@ -1,22 +1,33 @@ package com.huanshankeji.compose.material3.ext import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.text.ext.INLINE_TEXT_DEPRECATED_MESSAGE import com.huanshankeji.compose.material3.Text import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.Color /** * An explicit alias of [Text]. */ @Composable -inline fun MaterialText(text: String, modifier: Modifier = Modifier) = - Text(text, modifier) +inline fun MaterialText(text: String, modifier: Modifier = Modifier, color: Color? = null) = + Text(text, modifier, color) + +@Deprecated(INLINE_TEXT_DEPRECATED_MESSAGE, ReplaceWith("TaglessText(text)")) +@Composable +fun InlineText(text: String) = + TaglessText(text) /** - * Delegates to raw inline text without any element on JS / Compose HTML. - * @see com.huanshankeji.compose.foundation.text.ext.InlineBasicText + * Delegates to raw text without any element on JS / Compose HTML. + * @see com.huanshankeji.compose.foundation.text.ext.TaglessBasicText */ @Composable -expect fun InlineText(text: String) +expect fun TaglessText(text: String) + +@Composable +fun SpanText(text: String, modifier: Modifier = Modifier, color: Color? = null) = + Text(text, modifier, color) fun String.toTextWithModifier(): @Composable (Modifier) -> Unit = @@ -25,8 +36,8 @@ fun String.toTextWithModifier(): @Composable (Modifier) -> Unit = fun String?.toNullableTextWithModifier(): @Composable ((Modifier) -> Unit)? = this?.toTextWithModifier() -fun String.toInlineText(): @Composable () -> Unit = - { InlineText(this) } +fun String.toTaglessText(): @Composable () -> Unit = + { TaglessText(this) } -fun String?.toNullableInlineText(): @Composable (() -> Unit)? = - this?.toInlineText() +fun String?.toNullableTaglessText(): @Composable (() -> Unit)? = + this?.toTaglessText() diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.kt index ef397ade..985e6ba3 100644 --- a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.kt +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.kt @@ -1,6 +1,7 @@ package com.huanshankeji.compose.material3.lazy.ext import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.ext.matchPositionRelativeParentJsDom import com.huanshankeji.compose.material.icons.Icon import com.huanshankeji.compose.material3.ext.toNullableContentWithModifier import com.huanshankeji.compose.material3.ext.toNullableTextWithModifier @@ -31,8 +32,14 @@ expect class ListScope { expect class ItemScope +/** + * @param isInteractiveJsDom whether the item is interactive on JS DOM, + * aka whether it shows effects when the mouse pointer hovers above it or when it gets clicked. + * On the `androidx.compose` targets, use `Modifier.clickable` for the same effect. + */ class ListItemComponents( val contentModifier: Modifier = Modifier, + val isInteractiveJsDom: Boolean, val headline: @Composable (Modifier) -> Unit, val start: @Composable ((Modifier) -> Unit)? = null, val end: @Composable ((Modifier) -> Unit)? = null, @@ -42,6 +49,7 @@ class ListItemComponents( ) { constructor( contentModifier: Modifier = Modifier, + isInteractiveJsDom: Boolean, headline: String, start: Icon? = null, end: Icon? = null, @@ -50,6 +58,7 @@ class ListItemComponents( overline: String? = null ) : this( contentModifier, + isInteractiveJsDom, headline.toTextWithModifier(), start.toNullableContentWithModifier(), end.toNullableContentWithModifier(), @@ -59,6 +68,11 @@ class ListItemComponents( ) } +/** + * On JS DOM, if there isn't a parent with a fixed height + * and you want the list height to be constraint to the available space of the parent instead of expanding the parent, + * set "position:relative;" on the parent element and use [Modifier.matchPositionRelativeParentJsDom]. + */ @Composable expect fun List(modifier: Modifier = Modifier, content: ListScope.() -> Unit) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ProgressIndicator.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ProgressIndicator.js.kt new file mode 100644 index 00000000..d6dbb5b7 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ProgressIndicator.js.kt @@ -0,0 +1,23 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.html.material3.MdCircularProgress +import com.huanshankeji.compose.html.material3.MdLinearProgress +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs + +@Composable +actual fun LinearProgressIndicator(progress: () -> Float, modifier: Modifier) = + MdLinearProgress(value = progress(), attrs = modifier.toAttrs()) + +@Composable +actual fun LinearProgressIndicator(modifier: Modifier) = + MdLinearProgress(indeterminate = true, attrs = modifier.toAttrs()) + +@Composable +actual fun CircularProgressIndicator(progress: () -> Float, modifier: Modifier) = + MdCircularProgress(value = progress(), attrs = modifier.toAttrs()) + +@Composable +actual fun CircularProgressIndicator(modifier: Modifier) = + MdCircularProgress(indeterminate = true, attrs = modifier.toAttrs()) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Text.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Text.js.kt index 4d8e536a..da4a2b2c 100644 --- a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Text.js.kt +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Text.js.kt @@ -1,9 +1,12 @@ package com.huanshankeji.compose.material3 import androidx.compose.runtime.Composable -import com.huanshankeji.compose.foundation.text.BasicText +import androidx.compose.runtime.InternalComposeApi +import com.huanshankeji.compose.foundation.text.ext.CommonBasicText import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.graphics.Color @Composable -actual fun Text(text: String, modifier: Modifier) = - BasicText(text, modifier) +actual fun Text(text: String, modifier: Modifier, color: Color?) = + @OptIn(InternalComposeApi::class) + CommonBasicText(text, modifier, color) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/ExposedDropdownMenu.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/ExposedDropdownMenu.js.kt new file mode 100644 index 00000000..a4442414 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/ExposedDropdownMenu.js.kt @@ -0,0 +1,102 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import com.huanshankeji.compose.material.icons.Icons +import com.huanshankeji.compose.material.icons.filled.ArrowDropDown +import com.huanshankeji.compose.material3.Icon +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.draw.rotate +import com.varabyte.kobweb.compose.ui.attrsModifier +import com.varabyte.kobweb.compose.ui.toAttrs +import org.jetbrains.compose.web.attributes.AttrsScope +import org.w3c.dom.HTMLElement + +@Composable +actual fun ExposedDropdownMenuBox( + expanded: Boolean, + onExpandedChange: (Boolean) -> Unit, + modifier: Modifier, + content: @Composable ExposedDropdownMenuBoxScope.() -> Unit +) = + MdMenuBox(modifier) { + ExposedDropdownMenuBoxScope(remember { mutableStateOf(null) }, expanded, onExpandedChange).content() + // The menu doesn't show with this implementation. + /* + val (anchorElement, setAnchorElement) = remember { mutableStateOf(null) } + ExposedDropdownMenuBoxScope(anchorElement, setAnchorElement, expanded, onExpandedChange).content() + */ + } + +actual class ExposedDropdownMenuBoxScope( + val anchorElementState: MutableState, val expanded: Boolean, val onExpandedChange: (Boolean) -> Unit + //val anchorElement: HTMLElement?, val setAnchorElement: (HTMLElement?) -> Unit, val expanded: Boolean, val onExpandedChange: (Boolean) -> Unit // The menu doesn't show with this implementation. +) { + //var anchorElement by anchorElementState + //val (anchorElement, setAnchorElement) = anchorElementState + /* + companion object { + const val ANCHOR_ID = "anchor" + } + */ + + actual fun Modifier.menuAnchor(): Modifier = + platformModify { + attrsModifier { + //id(ANCHOR_ID) // An alternative approach by setting IDs. Duplicate IDs are semantically incorrect, however. + + refSetAnchorElementState(anchorElementState.component2()) + // only fired when `expanded` set to `true` to be consistent with the `androidx.compose` one behavior + if (!expanded) + onClick { + onExpandedChange(true) + } + } + } + + @Composable + actual fun ExposedDropdownMenu( + expanded: Boolean, + onDismissRequestAndroidx: () -> Unit, + onCloseJsDom: () -> Unit, + modifier: Modifier, + content: @Composable () -> Unit + ) = + CommonDropdownMenu( + //ANCHOR_ID, // An alternative approach by setting IDs. Duplicate IDs are semantically incorrect, however. + expanded, + onCloseJsDom, + // The following is identical to inlining `mdMenuModifier(anchorElementState.value, modifier)` but probably due to some Compose compiler bugs invoking it directly doesn't work. FIXME when the bug is fixed + { + ref { + it.anchorElement = anchorElementState.value + onDispose {} + } + + modifier.platformModifier.toAttrs>()() + }, + content = { content() } + //onExpandedChange = onExpandedChange // It seems there's no need to pass here as `onExpandedChange` will be called when clicking the text view + ) +} + +@Composable +actual fun ExposedDropdownMenuBoxScope.ExposedDropdownMenuBoxTextField( + expanded: Boolean, args: ExposedDropdownMenuBoxTextFieldArgs +) = + with(args) { + // adapted from the `androidxCommon` one + TextField( + modifier = Modifier.menuAnchor(/*MenuAnchorType.PrimaryNotEditable*/), + value = value, + onValueChange = onValueChange, + readOnly = readOnly, + singleLine = singleLine, + label = label, + trailingIcon = { modifier -> + Icon(Icons.Filled.ArrowDropDown, null, if (expanded) modifier.rotate(180f) else modifier) + } + ) + } diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Menu.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Menu.js.kt new file mode 100644 index 00000000..a8282443 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Menu.js.kt @@ -0,0 +1,161 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.* +import androidx.compose.ui.unit.DpOffset +import androidx.compose.web.events.SyntheticEvent +import com.huanshankeji.compose.html.material3.* +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs +import com.huanshankeji.compose.web.attributes.Attrs +import com.huanshankeji.compose.web.attributes.isFalseOrNull +import com.huanshankeji.compose.web.attributes.isTrueOrNull +import com.varabyte.kobweb.compose.ui.attrsModifier +import com.varabyte.kobweb.compose.ui.toAttrs +import org.jetbrains.compose.web.attributes.AttrsScope +import org.jetbrains.compose.web.css.Position +import org.jetbrains.compose.web.css.position +import org.jetbrains.compose.web.dom.Div +import org.jetbrains.compose.web.dom.ElementScope +import org.w3c.dom.HTMLDivElement +import org.w3c.dom.HTMLElement + +@Composable +internal fun CommonDropdownMenu( + //anchor: String?, + expanded: Boolean, + onCloseJsDom: () -> Unit, + //onDismissRequest: () -> Unit, + attrs: Attrs?, + offset: DpOffset = defaultDpOffset, + content: @Composable ElementScope.() -> Unit + //onExpandedChange: ((Boolean) -> Unit)? = null +) = + MdMenu( + open = expanded.isTrueOrNull(), + xOffset = offset.x.value.takeIf { it != 0f }, + yOffset = offset.y.value.takeIf { it != 0f }, + attrs = { + /*onExpandedChange?.let { + onOpening> { it(true) } + onClosing> { + onDismissRequest() + it(false) + } + }*/ + //onClosing> { onDismissRequest() } // the `androidx.compose` one invokes `onDismissRequest` only when the menu is closed and an item is not selected. + onClosing> { onCloseJsDom() } + + attrs?.invoke(this) + }, content = content + ) + +internal fun AttrsScope<*>.refSetAnchorElementState(setAnchorElement: (HTMLElement?) -> Unit) = + ref { + setAnchorElement(it as HTMLElement) + onDispose { setAnchorElement(null) } + } + +//@Suppress("NOTHING_TO_INLINE") +internal /*inline*/ fun AttrsScope.refSetMdMenuElementAnchorElement(anchorElement: HTMLElement?) = + ref { + //anchorElement?.let { anchorElement -> it.anchorElement = anchorElement } + it.anchorElement = anchorElement + onDispose {} + } + +//@Suppress("NOTHING_TO_INLINE") +internal /*inline*/ fun mdMenuAttrs( + anchorElement: HTMLElement?, + modifier: Modifier +): AttrsScope.() -> Unit = + { + refSetMdMenuElementAnchorElement(anchorElement) + + modifier.platformModifier.toAttrs>()() + } + +@Composable +internal fun MdMenuBox(modifier: Modifier, content: @Composable ElementScope.() -> Unit) = + Div( + { + // see https://github.com/material-components/material-web/blob/main/docs/components/menu.md#usage + style { + position(Position.Relative) + } + modifier.toAttrs>()() + }, + content = content + ) + + +@Composable +actual fun DropdownMenu( + expanded: Boolean, + onDismissRequestAndroidx: () -> Unit, + onCloseJsDom: () -> Unit, + modifier: Modifier, + offset: DpOffset, + content: @Composable () -> Unit +) = + CommonDropdownMenu(expanded, onCloseJsDom, modifier.toAttrs(), offset) { content() } + +actual class DropdownMenuBoxScope(anchorElementState: MutableState) { + var anchorElement by anchorElementState + actual fun Modifier.menuAnchorJsDom(): Modifier = + platformModify { + attrsModifier { + refSetAnchorElementState { anchorElement = it } + } + } + + @Composable + actual fun DropdownMenu( + expanded: Boolean, + onDismissRequestAndroidx: () -> Unit, + onCloseJsDom: () -> Unit, + modifier: Modifier, + offset: DpOffset, + content: @Composable () -> Unit + ) = + CommonDropdownMenu( + expanded, onCloseJsDom, + // The following is identical to inlining `mdMenuModifier(anchorElementState.value, modifier)` but probably due to some Compose compiler bugs invoking it directly doesn't work. FIXME when the bug is fixed + { + ref { + it.anchorElement = anchorElement + onDispose {} + } + + modifier.platformModifier.toAttrs>()() + }, offset + ) { content() } +} + +@Composable +actual fun DropdownMenuBox(content: @Composable DropdownMenuBoxScope.() -> Unit) = + MdMenuBox(Modifier) { + DropdownMenuBoxScope(remember { mutableStateOf(null) }).content() + } + +@Composable +actual fun DropdownMenuItem( + text: @Composable (Modifier) -> Unit, + onClick: () -> Unit, + modifier: Modifier, + leadingIcon: @Composable ((Modifier) -> Unit)?, + trailingIcon: @Composable ((Modifier) -> Unit)?, + enabled: Boolean, + keepOpenJsDom: Boolean +) = + MdMenuItem( + enabled.isFalseOrNull(), + keepOpen = keepOpenJsDom.isTrueOrNull(), + attrs = modifier.toAttrs { + onClick { + onClick() + } + }) { + text(Modifier.platformModify { attrsModifier { slot(MdMenuItemScope.Slot.Headline) } }) + leadingIcon?.let { it(Modifier.platformModify { attrsModifier { slot(MdMenuItemScope.Slot.Start) } }) } + trailingIcon?.let { it(Modifier.platformModify { attrsModifier { slot(MdMenuItemScope.Slot.End) } }) } + } diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.js.kt index 957eef75..2ddae8cb 100644 --- a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.js.kt +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.js.kt @@ -44,7 +44,9 @@ actual fun NavigationBarScope.NavigationBarItem( selected.isTrueOrNull(), alwaysShowLabel.isFalseOrNull(), label, - attrs = modifier.toAttrs() + attrs = modifier.toAttrs { + onClick { onClick() } + } ) { selectedIcon(PlatformModifier.attrsModifier { slot(MdNavigationTabScope.Slot.ActiveIcon) }.toCommonModifier()) unselectedIcon( diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Text.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Text.js.kt index d71b78f9..3745b801 100644 --- a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Text.js.kt +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Text.js.kt @@ -4,5 +4,5 @@ import androidx.compose.runtime.Composable import org.jetbrains.compose.web.dom.Text @Composable -actual fun InlineText(text: String) = +actual fun TaglessText(text: String) = Text(text) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.js.kt index 971f815a..03399e61 100644 --- a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.js.kt +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.js.kt @@ -8,6 +8,7 @@ import com.huanshankeji.compose.foundation.text.attrsFrom import com.huanshankeji.compose.html.material3.MdFilledTextField import com.huanshankeji.compose.html.material3.MdOutlinedTextField import com.huanshankeji.compose.html.material3.MdTextFieldScope +import com.huanshankeji.compose.html.material3.TextareaInputType import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.PlatformModifier import com.huanshankeji.compose.ui.toAttrs @@ -20,6 +21,10 @@ import com.varabyte.kobweb.compose.ui.attrsModifier import org.jetbrains.compose.web.attributes.AttrsScope import org.w3c.dom.HTMLElement +private fun inputType(singleLine: Boolean, lines: Int) = + // This is consistent with the `androidx.compose.material3` text field behavior. + if (singleLine || lines == 1) null else TextareaInputType + private fun Modifier.toTextFieldAttrs( onValueChange: (String) -> Unit, keyboardOptions: KeyboardOptions, keyboardActions: KeyboardActions, ): AttrsScope.() -> Unit = @@ -37,6 +42,7 @@ private fun TextFieldContent( trailingIcon: @Composable ((Modifier) -> Unit)?, ): @Composable MdTextFieldScope.() -> Unit = { with(elementScope) { + // This can't be put in `AttrsScope.ref` because it needs to run whenever `value` changes instead of just when the `HTMLElement` is added. DisposableEffect(value) { scopeElement.value = value onDispose {} @@ -72,7 +78,7 @@ actual fun TextField( isError.isTrueOrNull(), supportingText, // TODO Is passing `supportingText` as `errorText` correct? label, - value = value, + //value = value, // This causes the caret to be reset to the start whenever the value changes if the `type` attribute is set. And since the value is set in the `content` `DisposableEffect` it seems OK not skip it here. prefixText = prefix, suffixText = suffix, hasLeadingIcon = leadingIcon?.let { true }, @@ -81,6 +87,7 @@ actual fun TextField( rows = if (singleLine) null else lines, placeholder = placeholder, readOnly = readOnly.isTrueOrNull(), + type = inputType(singleLine, lines), attrs = modifier.toTextFieldAttrs(onValueChange, keyboardOptions, keyboardActions), content = TextFieldContent(value, leadingIcon, trailingIcon) @@ -112,7 +119,7 @@ actual fun OutlinedTextField( isError.isTrueOrNull(), supportingText, // TODO Is passing `supportingText` as `errorText` correct? label, - value = value, + //value = value, // This causes the caret to be reset to the start whenever the value changes if the `type` attribute is set. And since the value is set in the `content` `DisposableEffect` it seems OK not skip it here. prefixText = prefix, suffixText = suffix, hasLeadingIcon = leadingIcon?.let { true }, @@ -121,6 +128,7 @@ actual fun OutlinedTextField( rows = if (singleLine) null else lines, placeholder = placeholder, readOnly = readOnly.isTrueOrNull(), + type = inputType(singleLine, lines), attrs = modifier.toTextFieldAttrs(onValueChange, keyboardOptions, keyboardActions), content = TextFieldContent(value, leadingIcon, trailingIcon) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.js.kt index e39b0b81..7edb1b97 100644 --- a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.js.kt +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.js.kt @@ -1,9 +1,10 @@ package com.huanshankeji.compose.material3.lazy.ext import androidx.compose.runtime.Composable -import com.huanshankeji.compose.foundation.verticalScrollPlatformModifier +import com.huanshankeji.compose.foundation.imitateAndroidxLayoutVerticalScrollPlatformModifier import com.huanshankeji.compose.html.material3.MdList import com.huanshankeji.compose.html.material3.MdListItemScope +import com.huanshankeji.compose.html.material3.MdListItemType import com.huanshankeji.compose.html.material3.MdListScope import com.huanshankeji.compose.runtime.DeferredComposableRunner import com.huanshankeji.compose.ui.Modifier @@ -39,9 +40,12 @@ actual class ListScope(val mdListScope: MdListScope) { @Composable - private fun ListItem(content: ListItemComponents) = - mdListScope.MdListItem(attrs = content.contentModifier.toAttrs()) { - contentFromComponents(content) + private fun ListItem(components: ListItemComponents) = + mdListScope.MdListItem( + type = if (components.isInteractiveJsDom) MdListItemType.Button else null, + attrs = components.contentModifier.toAttrs() + ) { + contentFromComponents(components) } @@ -91,6 +95,6 @@ actual fun List( modifier: Modifier, content: ListScope.() -> Unit ) = - MdList(verticalScrollPlatformModifier.then(modifier.platformModifier).toAttrs()) { + MdList(imitateAndroidxLayoutVerticalScrollPlatformModifier.then(modifier.platformModifier).toAttrs()) { ListScope(this).ComposableRun(content) } diff --git a/compose-multiplatform-navigation/build.gradle.kts b/compose-multiplatform-navigation/build.gradle.kts new file mode 100644 index 00000000..4623c38d --- /dev/null +++ b/compose-multiplatform-navigation/build.gradle.kts @@ -0,0 +1,40 @@ +import com.huanshankeji.team.`Shreck Ye` +import com.huanshankeji.team.pomForTeamDefaultOpenSource + +plugins { + `lib-conventions` +} + +kotlin { + sourceSets { + commonMain { + dependencies { + /* + Use `api`. See: + https://github.com/JetBrains/compose-multiplatform-core/blob/jb-main/navigation/navigation-compose/build.gradle + https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/navigation/navigation-compose/build.gradle + */ + api(compose.runtime) + api(commonDependencies.jetbrainsAndroidx.navigation.runtime()) + //implementation("org.jetbrains.compose.annotation-internal:annotation:${DependencyVersions.composeMultiplatform}") + api(project(":compose-multiplatform-common")) // for `Modifier` and `Alignment` + //implementation("org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0") // This depends on Compose target '[jscanvas]'. + } + } + androidxCommonMain { + dependencies { + api(commonDependencies.jetbrainsAndroidx.navigation.compose()) + } + } + } +} + +publishing.publications.withType { + pomForTeamDefaultOpenSource( + project, + "Unified Compose Multiplatform navigation $FOR_COMPOSE_TARGETS_IN_TITLE", + "Unified wrappers of Compose Multiplatform navigation for $FOR_COMPOSE_TARGETS_IN_DESCRIPTION" + ) { + `Shreck Ye`() + } +} diff --git a/compose-multiplatform-navigation/src/androidxCommonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavGraphBuilder.androidxCommon.kt b/compose-multiplatform-navigation/src/androidxCommonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavGraphBuilder.androidxCommon.kt new file mode 100644 index 00000000..5127b005 --- /dev/null +++ b/compose-multiplatform-navigation/src/androidxCommonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavGraphBuilder.androidxCommon.kt @@ -0,0 +1,16 @@ +package com.huanshankeji.androidx.navigation.compose + +import androidx.compose.runtime.Composable +import androidx.navigation.NamedNavArgument +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavDeepLink +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable + +actual fun NavGraphBuilder.composable( + route: String, + arguments: List, + deepLinks: List, + content: @Composable (NavBackStackEntry) -> Unit +) = + composable(route, arguments, deepLinks) { content(it) } diff --git a/compose-multiplatform-navigation/src/androidxCommonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.androidxCommon.kt b/compose-multiplatform-navigation/src/androidxCommonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.androidxCommon.kt new file mode 100644 index 00000000..bfb1c72a --- /dev/null +++ b/compose-multiplatform-navigation/src/androidxCommonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.androidxCommon.kt @@ -0,0 +1,36 @@ +package com.huanshankeji.androidx.navigation.compose + +import androidx.compose.runtime.Composable +import androidx.navigation.NavGraph +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import com.huanshankeji.compose.ui.Alignment +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun NavHost( + navController: NavHostController, + startDestination: String, + modifier: Modifier, + contentAlignment: Alignment, + route: String?, + builder: NavGraphBuilder.() -> Unit +) = + NavHost( + navController, + startDestination, + modifier.platformModifier, + contentAlignment.platformAlignment, + route, + builder = builder + ) + +@Composable +actual fun NavHost( + navController: NavHostController, + graph: NavGraph, + modifier: Modifier, + contentAlignment: Alignment +) = + NavHost(navController, graph, modifier.platformModifier, contentAlignment.platformAlignment) diff --git a/compose-multiplatform-navigation/src/androidxCommonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHostController.androidxCommon.kt b/compose-multiplatform-navigation/src/androidxCommonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHostController.androidxCommon.kt new file mode 100644 index 00000000..146ce181 --- /dev/null +++ b/compose-multiplatform-navigation/src/androidxCommonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHostController.androidxCommon.kt @@ -0,0 +1,15 @@ +package com.huanshankeji.androidx.navigation.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.navigation.* +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController + +@Composable +actual fun NavController.currentBackStackEntryAsState(): State = + currentBackStackEntryAsState() + +@Composable +actual fun rememberNavController(vararg navigators: Navigator): NavHostController = + rememberNavController(*navigators) diff --git a/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/NavController.kt b/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/NavController.kt new file mode 100644 index 00000000..4c6f2a49 --- /dev/null +++ b/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/NavController.kt @@ -0,0 +1,28 @@ +package com.huanshankeji.androidx.navigation + +// It seems OK to not create a wrapper for this class and just use the one in `androidx.navigation`. +/* +expect class NavController { + @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) + val currentBackStack: StateFlow> + + @MainThread + fun popBackStack(): Boolean + @MainThread + fun navigateUp(): Boolean + + @MainThread + fun navigate(route: String, builder: NavOptionsBuilder.() -> Unit) + + @MainThread + //@JvmOverloads + fun navigate( + route: String, + navOptions: NavOptions? = null, + navigatorExtras: Navigator.Extras? = null + ) + + val currentBackStackEntry: NavBackStackEntry? +} +*/ diff --git a/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavGraphBuilder.kt b/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavGraphBuilder.kt new file mode 100644 index 00000000..6349e59a --- /dev/null +++ b/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavGraphBuilder.kt @@ -0,0 +1,14 @@ +package com.huanshankeji.androidx.navigation.compose + +import androidx.compose.runtime.Composable +import androidx.navigation.NamedNavArgument +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavDeepLink +import androidx.navigation.NavGraphBuilder + +expect fun NavGraphBuilder.composable( + route: String, + arguments: List = emptyList(), + deepLinks: List = emptyList(), + content: @Composable /*AnimatedContentScope.*/(NavBackStackEntry) -> Unit +) diff --git a/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.kt b/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.kt new file mode 100644 index 00000000..4d63297d --- /dev/null +++ b/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.kt @@ -0,0 +1,26 @@ +package com.huanshankeji.androidx.navigation.compose + +import androidx.compose.runtime.Composable +import androidx.navigation.NavGraph +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import com.huanshankeji.compose.ui.Alignment +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun NavHost( + navController: NavHostController, + startDestination: String, + modifier: Modifier = Modifier, + contentAlignment: Alignment = Alignment.TopStart, + route: String? = null, + builder: NavGraphBuilder.() -> Unit +) + +@Composable +expect fun NavHost( + navController: NavHostController, + graph: NavGraph, + modifier: Modifier = Modifier, + contentAlignment: Alignment = Alignment.TopStart, +) diff --git a/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHostController.kt b/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHostController.kt new file mode 100644 index 00000000..de636609 --- /dev/null +++ b/compose-multiplatform-navigation/src/commonMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHostController.kt @@ -0,0 +1,14 @@ +package com.huanshankeji.androidx.navigation.compose + + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.navigation.* + +@Composable +expect fun NavController.currentBackStackEntryAsState(): State + +@Composable +expect fun rememberNavController( + vararg navigators: Navigator +): NavHostController diff --git a/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/ComposeNavigator.kt b/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/ComposeNavigator.kt new file mode 100644 index 00000000..fcf7dfdc --- /dev/null +++ b/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/ComposeNavigator.kt @@ -0,0 +1,57 @@ +package com.huanshankeji.androidx.navigation.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavDestination +import androidx.navigation.NavOptions +import androidx.navigation.Navigator +import com.huanshankeji.androidx.navigation.compose.ComposeNavigator.Destination + +// copied and adapted from "ComposeNavigator.kt" in `androidx.navigation.compose` + +/*actual*/ class ComposeNavigator : Navigator(NAME) { + internal /*actual*/ val transitionsInProgress get() = state.transitionsInProgress + + /*actual*/ val backStack get() = state.backStack + + internal /*actual*/ val isPop = mutableStateOf(false) + + override fun navigate( + entries: List, + navOptions: NavOptions?, + navigatorExtras: Extras? + ) { + entries.forEach { entry -> + state.pushWithTransition(entry) + } + isPop.value = false + } + + override fun createDestination(): Destination { + return Destination(this) { } + } + + override fun popBackStack(popUpTo: NavBackStackEntry, savedState: Boolean) { + state.popWithTransition(popUpTo, savedState) + isPop.value = true + } + + /*actual*/ fun prepareForTransition(entry: NavBackStackEntry) { + state.prepareForTransition(entry) + } + + /*actual*/ fun onTransitionComplete(entry: NavBackStackEntry) { + state.markTransitionComplete(entry) + } + + /*actual*/ class Destination /*actual constructor*/( + navigator: ComposeNavigator, + internal /*actual*/ val content: + @Composable /*AnimatedContentScope.*/(/*@JvmSuppressWildcards*/ NavBackStackEntry) -> Unit + ) : NavDestination(navigator) + + internal /*actual*/ companion object { + internal /*actual*/ const val NAME = "composable" + } +} diff --git a/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavGraphBuilder.js.kt b/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavGraphBuilder.js.kt new file mode 100644 index 00000000..7d61568e --- /dev/null +++ b/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavGraphBuilder.js.kt @@ -0,0 +1,28 @@ +package com.huanshankeji.androidx.navigation.compose + +import androidx.compose.runtime.Composable +import androidx.navigation.* + +// copied and adapted from "NavGraphBuilder.kt" in `androidx.navigation.compose` + +actual fun NavGraphBuilder.composable( + route: String, + arguments: List, + deepLinks: List, + content: @Composable (NavBackStackEntry) -> Unit +) { + addDestination( + ComposeNavigator.Destination( + provider[ComposeNavigator.NAME], + content + ).apply { + this.route = route + arguments.forEach { (argumentName, argument) -> + addArgument(argumentName, argument) + } + deepLinks.forEach { deepLink -> + addDeepLink(deepLink) + } + } + ) +} diff --git a/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt b/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt new file mode 100644 index 00000000..3ef23d1a --- /dev/null +++ b/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt @@ -0,0 +1,139 @@ +package com.huanshankeji.androidx.navigation.compose + +import androidx.compose.runtime.* +import androidx.navigation.* +import com.huanshankeji.compose.foundation.layout.Box +import com.huanshankeji.compose.foundation.layout.fillMaxSize +import com.huanshankeji.compose.ui.Alignment +import com.huanshankeji.compose.ui.Modifier + +// copied and adapted from "NavHost.kt" in `androidx.navigation.compose` + + +/* +private class ComposeViewModelStoreOwner: ViewModelStoreOwner { + override val viewModelStore: ViewModelStore = ViewModelStore() + fun dispose() { viewModelStore.clear() } +} + +@Composable +private fun rememberViewModelStoreOwner(): ViewModelStoreOwner { + val viewModelStoreOwner = remember { ComposeViewModelStoreOwner() } + DisposableEffect(viewModelStoreOwner) { + onDispose { viewModelStoreOwner.dispose() } + } + return viewModelStoreOwner +} +*/ + + + +@Composable +actual fun NavHost( + navController: NavHostController, + startDestination: String, + modifier: Modifier, + contentAlignment: Alignment, + route: String?, + builder: NavGraphBuilder.() -> Unit +) { + NavHost( + navController, + remember(route, startDestination, builder) { + navController.createGraph(startDestination, route, builder) + }, + modifier, + contentAlignment + ) +} + +@Composable +actual fun NavHost( + navController: NavHostController, + graph: NavGraph, + modifier: Modifier, + contentAlignment: Alignment, +) { + + //val lifecycleOwner = LocalLifecycleOwner.current + //val viewModelStoreOwner = LocalViewModelStoreOwner.current ?: rememberViewModelStoreOwner() + + //navController.setViewModelStore(viewModelStoreOwner.viewModelStore) + + // Then set the graph + navController.graph = graph + + // Find the ComposeNavigator, returning early if it isn't found + // (such as is the case when using TestNavHostController) + val composeNavigator = navController.navigatorProvider.get>( + ComposeNavigator.NAME + ) as? ComposeNavigator ?: return + + //val currentBackStack by composeNavigator.backStack.collectAsState() + +// BackHandler(currentBackStack.size > 1) { +// navController.popBackStack() +// } + + /* + DisposableEffect(lifecycleOwner) { + // Setup the navController with proper owners + navController.setLifecycleOwner(lifecycleOwner) + onDispose { } + } + */ + + //val saveableStateHolder = rememberSaveableStateHolder() + + val allVisibleEntries by navController.visibleEntries.collectAsState() + + // Intercept back only when there's a destination to pop + val visibleEntries by remember { + derivedStateOf { + allVisibleEntries.filter { entry -> + entry.destination.navigatorName == ComposeNavigator.NAME + } + } + } + + val backStackEntry: NavBackStackEntry? = visibleEntries.lastOrNull() + + if (backStackEntry != null) { + // `fillMaxSize` is added here to make the Box align to the size of its parent + // TODO consider adding a version of `NavHost` without `modifier` and `contentAlignment` + // Originally it was `transition.AnimatedContent` here. + Box(modifier.fillMaxSize(), contentAlignment) { + val currentEntry = visibleEntries.lastOrNull { entry -> backStackEntry == entry } + + /* + // while in the scope of the composable, we provide the navBackStackEntry as the + // ViewModelStoreOwner and LifecycleOwner + currentEntry?.LocalOwnersProvider(saveableStateHolder) { + (currentEntry.destination as ComposeNavigator.Destination) + .content( currentEntry) + } + */ + currentEntry?.let { + (it.destination as ComposeNavigator.Destination) + .content(currentEntry) + } + } + + DisposableEffect(true) { + onDispose { + visibleEntries.forEach { entry -> + composeNavigator.onTransitionComplete(entry) + } + } + } + } + + /* + val dialogNavigator = navController.navigatorProvider.get>( + DialogNavigator.NAME + ) as? DialogNavigator ?: return + + // Show any dialog destinations + DialogHost(dialogNavigator) + */ +} diff --git a/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHostController.js.kt b/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHostController.js.kt new file mode 100644 index 00000000..80cf3f4a --- /dev/null +++ b/compose-multiplatform-navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHostController.js.kt @@ -0,0 +1,35 @@ +package com.huanshankeji.androidx.navigation.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.navigation.* + +// copied and adapted from "NavHostController.jb.kt" in `androidx.navigation.compose` + +@Composable +actual fun NavController.currentBackStackEntryAsState(): State { + return currentBackStackEntryFlow.collectAsState(null) +} + +@Composable +actual fun rememberNavController( + vararg navigators: Navigator +): NavHostController { + // `rememberSaveable` seems not needed here + return remember { + createNavController() + }.apply { + for (navigator in navigators) { + navigatorProvider.addNavigator(navigator) + } + } +} + +private fun createNavController() = + NavHostController().apply { + //navigatorProvider.addNavigator(ComposeNavGraphNavigator(navigatorProvider)) + navigatorProvider.addNavigator(ComposeNavigator()) + //navigatorProvider.addNavigator(DialogNavigator()) + } diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts index 00f7929a..227ec7e9 100644 --- a/demo/build.gradle.kts +++ b/demo/build.gradle.kts @@ -48,6 +48,12 @@ kotlin { implementation(compose.runtime) implementation(project(":compose-multiplatform-material2")) implementation(project(":compose-multiplatform-material3")) + implementation(project(":compose-multiplatform-navigation")) + /* + see https://github.com/JetBrains/compose-multiplatform-core/blob/476d43b99a27696d12ef087e8028d90789645ba7/compose/ui/ui/build.gradle#L54 + and https://github.com/JetBrains/compose-multiplatform-core/blob/381796b5e682653aa1fa53e6bcf0441d06b873f8/compose/runtime/runtime/build.gradle#L124 + */ + implementation(commonDependencies.kotlinx.coroutines.core()) } } jvmMain { @@ -60,8 +66,8 @@ kotlin { // TODO consider putting this in `androidxCommonMain` implementation(compose.ui) - implementation("androidx.activity:activity-compose:${DependencyVersions.Androidx.activityCompose}") - implementation("androidx.compose.ui:ui-tooling-preview:${DependencyVersions.Androidx.compose}") + implementation(commonDependencies.androidx.activity.compose()) + implementation(commonDependencies.androidx.compose.ui.module("tooling-preview")) } } iosMain { @@ -91,16 +97,12 @@ compose { mainClass = "$`package`.MainKt" } } - - experimental { - web.application {} - } } android { namespace = `package` - val sdk = 34 + val sdk = androidSdkVersion compileSdk = sdk defaultConfig { @@ -109,4 +111,11 @@ android { targetSdk = sdk versionName = version as String } + + buildFeatures { + compose = true + } + dependencies { + debugImplementation(compose.uiTooling) + } } diff --git a/demo/iosApp/iosApp.xcodeproj/project.pbxproj b/demo/iosApp/iosApp.xcodeproj/project.pbxproj index 67e35603..8831f318 100644 --- a/demo/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/demo/iosApp/iosApp.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 56; objects = { /* Begin PBXBuildFile section */ @@ -17,7 +17,7 @@ 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; - 7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF7B242A565900829871 /* compose-multiplatform-material-demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = compose-multiplatform-material-demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; @@ -62,7 +62,7 @@ 7555FF7C242A565900829871 /* Products */ = { isa = PBXGroup; children = ( - 7555FF7B242A565900829871 /* iosApp.app */, + 7555FF7B242A565900829871 /* compose-multiplatform-material-demo.app */, ); name = Products; sourceTree = ""; @@ -107,7 +107,7 @@ packageProductDependencies = ( ); productName = iosApp; - productReference = 7555FF7B242A565900829871 /* iosApp.app */; + productReference = 7555FF7B242A565900829871 /* compose-multiplatform-material-demo.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -116,8 +116,9 @@ 7555FF73242A565900829871 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1130; - LastUpgradeCheck = 1130; + LastUpgradeCheck = 1540; ORGANIZATIONNAME = orgName; TargetAttributes = { 7555FF7A242A565900829871 = { @@ -126,7 +127,7 @@ }; }; buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */; - compatibilityVersion = "Xcode 12.0"; + compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -229,6 +230,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -291,6 +293,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -319,6 +322,7 @@ DEVELOPMENT_TEAM = "${TEAM_ID}"; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", "$(SRCROOT)/../build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", ); INFOPLIST_FILE = iosApp/Info.plist; @@ -330,7 +334,7 @@ OTHER_LDFLAGS = ( "$(inherited)", "-framework", - composeApp, + ComposeApp, ); PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; PRODUCT_NAME = "${APP_NAME}"; @@ -361,7 +365,7 @@ OTHER_LDFLAGS = ( "$(inherited)", "-framework", - composeApp, + ComposeApp, ); PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; PRODUCT_NAME = "${APP_NAME}"; @@ -395,4 +399,4 @@ /* End XCConfigurationList section */ }; rootObject = 7555FF73242A565900829871 /* Project object */; -} +} \ No newline at end of file diff --git a/demo/src/androidMain/AndroidManifest.xml b/demo/src/androidMain/AndroidManifest.xml index 259e6ff6..16cd537c 100644 --- a/demo/src/androidMain/AndroidManifest.xml +++ b/demo/src/androidMain/AndroidManifest.xml @@ -1,20 +1,21 @@ - + + android:allowBackup="true" + android:label="@string/app_name" + android:supportsRtl="true" + android:theme="@android:style/Theme.Material.Light.NoActionBar" + tools:ignore="MissingApplicationIcon"> + android:name=".MainActivity" + android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|mnc|colorMode|density|fontScale|fontWeightAdjustment|keyboard|layoutDirection|locale|mcc|navigation|smallestScreenSize|touchscreen|uiMode" + android:exported="true"> - + - + diff --git a/demo/src/androidMain/kotlin/com/huanshankeji/compose/material/demo/MainActivity.kt b/demo/src/androidMain/kotlin/com/huanshankeji/compose/material/demo/MainActivity.kt index 4095383e..b14010f5 100644 --- a/demo/src/androidMain/kotlin/com/huanshankeji/compose/material/demo/MainActivity.kt +++ b/demo/src/androidMain/kotlin/com/huanshankeji/compose/material/demo/MainActivity.kt @@ -18,4 +18,3 @@ class MainActivity : ComponentActivity() { @Composable fun AppAndroidPreview() = App() - diff --git a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/App.kt b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/App.kt index 90513c41..847b22c1 100644 --- a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/App.kt +++ b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/App.kt @@ -2,34 +2,60 @@ package com.huanshankeji.compose.material.demo import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import com.huanshankeji.androidx.navigation.compose.NavHost +import com.huanshankeji.androidx.navigation.compose.composable +import com.huanshankeji.androidx.navigation.compose.rememberNavController +import com.huanshankeji.compose.foundation.layout.Arrangement import com.huanshankeji.compose.foundation.layout.Box -import com.huanshankeji.compose.foundation.layout.Row -import com.huanshankeji.compose.foundation.rememberScrollState -import com.huanshankeji.compose.foundation.verticalScroll -import com.huanshankeji.compose.layout.padding +import com.huanshankeji.compose.foundation.layout.Column +import com.huanshankeji.compose.foundation.layout.ext.fillMaxSizeStretch +import com.huanshankeji.compose.foundation.layout.ext.innerPadding +import com.huanshankeji.compose.foundation.layout.ext.outerPadding +import com.huanshankeji.compose.material3.Button +import com.huanshankeji.compose.material3.ext.TaglessText +import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier -internal enum class RadioButtonState { +internal enum class Selection { A, B, C } val listSize = 160.dp -fun Modifier.contentPadding() = padding(16.dp) -val contentPaddingModifier = Modifier.contentPadding() +fun Modifier.outerContentPadding() = outerPadding(16.dp) +fun Modifier.innerContentPadding() = innerPadding(16.dp) +val contentPaddingModifier = Modifier.outerContentPadding() + +enum class Screen { + Home, Common, Material2, Material3 +} @Composable fun App() { - Row(/*Modifier.height(720.dp)*/) { - @Composable - fun subDemoModifier() = - Modifier.weight(1f).verticalScroll(rememberScrollState()) + val navController = rememberNavController() + NavHost(navController, Screen.Home.name) { + composable(Screen.Home.name) { Home(navController) } + //fun subDemoModifier() + composable(Screen.Common.name) { Common() } + composable(Screen.Material2.name) { Material2() } + composable(Screen.Material3.name) { Material3() } + } +} - Common(subDemoModifier()) - // Putting the scroll modifier in the `Box` causes `java.lang.IllegalArgumentException: Can't represent a size of 2147483577 in Constraints`. - Box(Modifier.weight(1f)) { - Material2(Modifier.verticalScroll(rememberScrollState())) +@Composable +fun Home(navController: NavHostController) { + Box(Modifier.fillMaxSizeStretch(), contentAlignment = Alignment.Center) { + Column(verticalArrangement = Arrangement.spacedBy(16.dp)) { + Button({ navController.navigate(Screen.Common.name) }) { + TaglessText("Common") + } + Button({ navController.navigate(Screen.Material2.name) }) { + TaglessText("Material 2") + } + Button({ navController.navigate(Screen.Material3.name) }) { + TaglessText("Material 3") + } } - Material3(subDemoModifier()) } } diff --git a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Common.kt b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Common.kt index 54120689..2b211d32 100644 --- a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Common.kt +++ b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Common.kt @@ -6,23 +6,21 @@ import com.huanshankeji.compose.foundation.* import com.huanshankeji.compose.foundation.ext.outerBorder import com.huanshankeji.compose.foundation.ext.roundedCornerBackgroundAndOuterBorder import com.huanshankeji.compose.foundation.ext.roundedCornerOuterBorder -import com.huanshankeji.compose.foundation.layout.Box -import com.huanshankeji.compose.foundation.layout.Column -import com.huanshankeji.compose.foundation.layout.Row +import com.huanshankeji.compose.foundation.layout.* import com.huanshankeji.compose.foundation.lazy.LazyColumn import com.huanshankeji.compose.foundation.lazy.LazyListScope import com.huanshankeji.compose.foundation.lazy.LazyRow import com.huanshankeji.compose.foundation.text.BasicText -import com.huanshankeji.compose.layout.height -import com.huanshankeji.compose.layout.padding -import com.huanshankeji.compose.layout.size -import com.huanshankeji.compose.layout.width +import com.huanshankeji.compose.layout.ext.hidden import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.graphics.Color @Composable -fun Common(modifier: Modifier) { - Column(modifier) { +fun Common(/*modifier: Modifier = Modifier*/) { + Column( + Modifier.verticalScroll(rememberScrollState()).innerContentPadding(), + Arrangement.spacedBy(16.dp) + ) { BasicText("basic text 1") BasicText("basic text 2") @@ -55,7 +53,8 @@ fun Common(modifier: Modifier) { } var count by remember { mutableStateOf(0) } - BasicText("Click to add items", Modifier.onClick { count++ }) + @OptIn(ExperimentalFoundationApi::class) + BasicText("Click to add items", Modifier.clickable { count++ }) val lazyListContent: LazyListScope.() -> Unit = { item { BasicText("Item") } items(count) { index -> BasicText("Item $index") } @@ -78,5 +77,9 @@ fun Common(modifier: Modifier) { Row(Modifier.width(listSize).horizontalScroll(rememberScrollState())) { ColumnOrRowContent() } + + BasicText("shown text") + BasicText("hidden text", Modifier.hidden()) + BasicText("shown text") } } \ No newline at end of file diff --git a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material2.kt b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material2.kt index eb7186c7..88902f9f 100644 --- a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material2.kt +++ b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material2.kt @@ -2,19 +2,17 @@ package com.huanshankeji.compose.material.demo import androidx.compose.runtime.* import androidx.compose.ui.unit.dp +import com.huanshankeji.compose.foundation.VerticalScrollBox import com.huanshankeji.compose.foundation.background -import com.huanshankeji.compose.foundation.layout.Column -import com.huanshankeji.compose.foundation.layout.Row -import com.huanshankeji.compose.foundation.layout.RowScope +import com.huanshankeji.compose.foundation.layout.* import com.huanshankeji.compose.foundation.text.KeyboardActions import com.huanshankeji.compose.foundation.text.KeyboardOptions import com.huanshankeji.compose.foundation.text.input.ImeAction import com.huanshankeji.compose.foundation.text.input.KeyboardCapitalization import com.huanshankeji.compose.foundation.text.input.KeyboardType -import com.huanshankeji.compose.layout.height -import com.huanshankeji.compose.layout.padding import com.huanshankeji.compose.material.icons.Icons import com.huanshankeji.compose.material.icons.filled.Add +import com.huanshankeji.compose.material.icons.filled.Done import com.huanshankeji.compose.material.icons.filled.Menu import com.huanshankeji.compose.material.icons.filled.Search import com.huanshankeji.compose.material2.* @@ -25,112 +23,130 @@ import com.huanshankeji.compose.material2.lazy.ext.conventionalItem import com.huanshankeji.compose.material2.lazy.ext.conventionalItems import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.ui.graphics.Color +import kotlinx.coroutines.launch import com.huanshankeji.compose.material2.ext.Button as ExtButton @Composable -fun Material2(modifier: Modifier) { +fun Material2(/*modifier: Modifier = Modifier*/) { + val snackbarHostState = remember { SnackbarHostState() } // It seems the modifier can't be set on `TopAppBarScaffold` or a box wrapping it TopAppBarScaffold({ Text("Compose Multiplatform Material demo") }, navigationIcon = { - MaterialIconNavButton({}, Icons.Default.Menu, "menu") + MaterialIconNavButton({}, icon = Icons.Default.Menu, contentDescription = "menu") }, actions = { - MaterialIconActionButton({}, Icons.Default.Search, "search") + MaterialIconActionButton({}, icon = Icons.Default.Search, contentDescription = "search") + MaterialIconActionButton({}, icon = Icons.Default.Done, contentDescription = "done") + }, bottomBar = { + Text("Bottom bar") // The Material 2 bottom bar is not added so this is a placeholder. + }, snackbarHost = { + SnackbarHost(snackbarHostState) + }, floatingActionButton = { + Text("FAB") // The Material 2 FAB is not added so this is a placeholder. }) { - Card(modifier.contentPadding()) { - Column(contentPaddingModifier.background(Color(0xF8, 0xF8, 0xF8, 0xFF))) { - Text("Material text") + VerticalScrollBox { + Card(contentPaddingModifier) { + Column(contentPaddingModifier.background(Color(0xF8, 0xF8, 0xF8, 0xFF)), Arrangement.spacedBy(16.dp)) { + Text("Material text") - var count by remember { mutableStateOf(0) } - val onClick: () -> Unit = { count++ } - val buttonContent: @Composable () -> Unit = { - InlineText(count.toString()) - } - val rowScopeButtonContent: @Composable RowScope.() -> Unit = { buttonContent() } - - Row { - Button(onClick, content = rowScopeButtonContent) - OutlinedButton(onClick, content = rowScopeButtonContent) - TextButton(onClick, content = rowScopeButtonContent) - ExtButton(onClick) { - Label(count.toString()) + var count by remember { mutableStateOf(0) } + val coroutineScope = rememberCoroutineScope() + val onClick: () -> Unit = { + count++ + val count = count + coroutineScope.launch { + snackbarHostState.showSnackbar("Count incremented to $count", "action") + } } - IconButton(onClick, icon = Icons.Default.Add, contentDescription = "increment count") - } - - val listModifier = Modifier.padding(16.dp).height(listSize) - List(listModifier) { - item { - Text("Ungrouped item") + val buttonContent: @Composable () -> Unit = { + TaglessText(count.toString()) } - items(count) { - Text("Ungrouped item $it/$count") + val rowScopeButtonContent: @Composable RowScope.() -> Unit = { buttonContent() } + + Row { + Button(onClick, content = rowScopeButtonContent) + OutlinedButton(onClick, content = rowScopeButtonContent) + TextButton(onClick, content = rowScopeButtonContent) + ExtButton(onClick) { + Label(count.toString()) + } + IconButton(onClick, icon = Icons.Default.Add, contentDescription = "increment count") } - group(headerContent = { - Text("Group title") - }) { + + val listModifier = Modifier.padding(16.dp).height(listSize) + List(listModifier) { item { - Text("Grouped item") + Text("Ungrouped item") } items(count) { - Text("Grouped item $it/$count") + Text("Ungrouped item $it/$count") + } + group(headerContent = { + Text("Group title") + }) { + item { + Text("Grouped item") + } + items(count) { + Text("Grouped item $it/$count") + } } } - } - Divider() - val secondaryText = "Secondary text" - List(listModifier) { - conventionalItem(content = ListItemComponents("Ungrouped item", secondaryText)) - conventionalItems(count) { - ListItemComponents("Ungrouped item $it/$count", secondaryText) - } - group(headerContent = { - Text("Group title") - }) { - conventionalItem(content = ListItemComponents("Grouped item", secondaryText)) + Divider() + val secondaryText = "Secondary text" + List(listModifier) { + conventionalItem(content = ListItemComponents("Ungrouped item", secondaryText)) conventionalItems(count) { - ListItemComponents("Grouped item $it/$count", secondaryText) + ListItemComponents("Ungrouped item $it/$count", secondaryText) + } + group(headerContent = { + Text("Group title") + }) { + conventionalItem(content = ListItemComponents("Grouped item", secondaryText)) + conventionalItems(count) { + ListItemComponents("Grouped item $it/$count", secondaryText) + } } } - } - var text by remember { mutableStateOf("") } - TextFieldWithMaterialIcons( - text, { text = it }, - label = "Demo text field", - leadingIcon = Icons.Default.Add, - trailingIcon = Icons.Default.Menu, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - singleLine = true - ) - OutlinedTextFieldWithMaterialIcons( - text, { text = it }, - label = "Demo text field", - leadingIcon = Icons.Default.Add, - trailingIcon = Icons.Default.Menu, - keyboardOptions = KeyboardOptions( - KeyboardCapitalization.Words, true, imeAction = ImeAction.Search - ), - keyboardActions = KeyboardActions { - println("keyboard actions with: $text") - }, - singleLine = true - ) - TextArea(text, { text = it }, label = "Demo text field", lines = 3) + var text by remember { mutableStateOf("") } + TextFieldWithMaterialIcons( + text, { text = it }, + label = "Demo text field", + leadingIcon = Icons.Default.Add, + trailingIcon = Icons.Default.Menu, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + singleLine = true + ) + OutlinedTextFieldWithMaterialIcons( + text, { text = it }, + label = "Demo text field", + leadingIcon = Icons.Default.Add, + trailingIcon = Icons.Default.Menu, + keyboardOptions = KeyboardOptions( + KeyboardCapitalization.Words, true, imeAction = ImeAction.Search + ), + keyboardActions = KeyboardActions { + println("keyboard actions with: $text") + }, + singleLine = true + ) + TextArea(text, { text = it }, label = "Demo text field", lines = 3) - var selected by remember { mutableStateOf(RadioButtonState.A) } - RadioGroupRow { - @Composable - fun RadioButtonRow(state: RadioButtonState) = - RadioRow(selected == state, state.toString(), { selected = state }) - RadioButtonState.entries.forEach { RadioButtonRow(it) } - } + var selected by remember { mutableStateOf(Selection.A) } + RadioGroupRow { + @Composable + fun RadioButtonRow(state: Selection) = + RadioRow(selected == state, state.toString(), { selected = state }) + Selection.entries.forEach { RadioButtonRow(it) } + } - Row { - var checked by remember { mutableStateOf(false) } - Checkbox(checked, { checked = it }) - Switch(checked, { checked = it }) - SwitchWithLabel(checked, { checked = it }, "Switch") + Row { + var checked by remember { mutableStateOf(false) } + Checkbox(checked, { checked = it }) + Switch(checked, { checked = it }) + SwitchWithLabel(checked, { checked = it }, "Switch") + } } } } diff --git a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt index 8d97f876..a32c2d6e 100644 --- a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt +++ b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt @@ -1,18 +1,19 @@ package com.huanshankeji.compose.material.demo import androidx.compose.runtime.* +import androidx.compose.ui.unit.dp import com.huanshankeji.compose.ExtRecommendedApi -import com.huanshankeji.compose.foundation.layout.Column -import com.huanshankeji.compose.foundation.layout.Row -import com.huanshankeji.compose.foundation.layout.RowScope +import com.huanshankeji.compose.foundation.layout.* +import com.huanshankeji.compose.foundation.rememberScrollState import com.huanshankeji.compose.foundation.text.KeyboardActions import com.huanshankeji.compose.foundation.text.KeyboardOptions import com.huanshankeji.compose.foundation.text.input.ImeAction import com.huanshankeji.compose.foundation.text.input.KeyboardCapitalization import com.huanshankeji.compose.foundation.text.input.KeyboardType -import com.huanshankeji.compose.layout.height +import com.huanshankeji.compose.foundation.verticalScroll import com.huanshankeji.compose.material.icons.Icons import com.huanshankeji.compose.material.icons.filled.Add +import com.huanshankeji.compose.material.icons.filled.ArrowDropDown import com.huanshankeji.compose.material.icons.filled.Menu import com.huanshankeji.compose.material.icons.filled.Remove import com.huanshankeji.compose.material3.* @@ -26,12 +27,12 @@ import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.material3.Button as RowScopeButton @Composable -fun Material3(modifier: Modifier) { - Column(modifier) { +fun Material3(/*modifier: Modifier = Modifier*/) { + Column(Modifier.verticalScroll(rememberScrollState()).innerContentPadding(), Arrangement.spacedBy(16.dp)) { var count by remember { mutableStateOf(0) } val onClick: () -> Unit = { count++ } val buttonContent: @Composable () -> Unit = { - InlineText(count.toString()) + TaglessText(count.toString()) } val rowScopeButtonContent: @Composable RowScope.() -> Unit = { buttonContent() } Row { @@ -140,12 +141,14 @@ fun Material3(modifier: Modifier) { println("keyboard actions with: $text") } ) + OutlinedTextField(text, { text = it }, label = label, placeholder = placeholder, lines = 2) Text("Click a button to show the list:") List(Modifier.height(listSize)) { fun content(index: String) = ListItemComponents( Modifier, + true, "Headline $index", Icons.Default.Add, Icons.Default.Menu, @@ -180,5 +183,50 @@ fun Material3(modifier: Modifier) { label = "Remove" ) } + + @Composable + fun DropdownMenuContent(setSelection: (Selection?) -> Unit, close: () -> Unit) = + (listOf(null) + Selection.entries).forEach { + DropdownMenuItemWithMaterialIcons( + { modifier -> it?.let { Text(it.name, modifier) } }, + { + setSelection(it) + close() + }, + leadingIcon = Icons.Filled.Add, + trailingIcon = Icons.Filled.Remove + ) + } + + run { + val (expanded, setExpanded) = remember { mutableStateOf(false) } + val close = { setExpanded(false) } + val (selection, setSelection) = remember { mutableStateOf(null) } + ExposedDropdownMenuBoxWithTextField( + expanded, setExpanded, + textFieldArgs = ExposedDropdownMenuBoxTextFieldArgs( + selection?.name ?: "", label = "Please select" + ), + exposedDropdownMenuArgs = ExposedDropdownMenuArgs(expanded, close, close) { + DropdownMenuContent(setSelection, close) + } + ) + } + DropdownMenuBox { + var expanded by remember { mutableStateOf(false) } + val close = { expanded = false } + val (_, setSelection) = remember { mutableStateOf(null) } + IconButton({ expanded = true }, Modifier.menuAnchorJsDom()) { + Icon(Icons.Filled.ArrowDropDown, "Please select") + } + DropdownMenu(expanded, close, close) { + DropdownMenuContent(setSelection, close) + } + } + + LinearProgressIndicator() + LinearProgressIndicator({ 0.5f }) + CircularProgressIndicator() + CircularProgressIndicator({ 0.5f }) } } diff --git a/demo/src/jsMain/resources/index.html b/demo/src/jsMain/resources/index.html index 1913ade4..4687f55c 100644 --- a/demo/src/jsMain/resources/index.html +++ b/demo/src/jsMain/resources/index.html @@ -5,9 +5,11 @@ Compose Multiplatform Material demo + diff --git a/demo/src/wasmJsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt b/demo/src/wasmJsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt index d2adf0e9..8912830e 100644 --- a/demo/src/wasmJsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt +++ b/demo/src/wasmJsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt @@ -1,9 +1,10 @@ package com.huanshankeji.compose.material.demo import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.window.CanvasBasedWindow +import androidx.compose.ui.window.ComposeViewport +import kotlinx.browser.document @OptIn(ExperimentalComposeUiApi::class) fun main() { - CanvasBasedWindow(canvasElementId = "ComposeTarget") { App() } + ComposeViewport(document.body!!) { App() } } diff --git a/demo/src/wasmJsMain/resources/index.html b/demo/src/wasmJsMain/resources/index.html index 5dd57c35..b473e4f7 100644 --- a/demo/src/wasmJsMain/resources/index.html +++ b/demo/src/wasmJsMain/resources/index.html @@ -3,12 +3,13 @@ + Compose Multiplatform Material demo + - \ No newline at end of file diff --git a/demo/src/wasmJsMain/resources/styles.css b/demo/src/wasmJsMain/resources/styles.css new file mode 100644 index 00000000..0549b10f --- /dev/null +++ b/demo/src/wasmJsMain/resources/styles.css @@ -0,0 +1,7 @@ +html, body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 09ecf666..39977236 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,6 @@ # For Wasm # More memory is needed now to compile the Wasm target in Gradle org.gradle.jvmargs=-Xmx2G -org.jetbrains.compose.experimental.wasm.enabled=true # For the `androidxCommon` custom source sets #kotlin.mpp.applyDefaultHierarchyTemplate=false # For Android diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd491..a4b76b95 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2ea3535d..79eb9d00 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a42..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 25da30db..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index 56c58375..40665701 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -12,6 +12,18 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -62,6 +74,26 @@ resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== +"@jsonjoy.com/base64@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/base64/-/base64-1.1.2.tgz#cf8ea9dcb849b81c95f14fc0aaa151c6b54d2578" + integrity sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA== + +"@jsonjoy.com/json-pack@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/json-pack/-/json-pack-1.0.4.tgz#ab59c642a2e5368e8bcfd815d817143d4f3035d0" + integrity sha512-aOcSN4MeAtFROysrbqG137b7gaDDSmVrl5mpo6sT/w+kcXpWnzhMjmY/Fh/sDx26NBxyIE7MB1seqLeCAzy9Sg== + dependencies: + "@jsonjoy.com/base64" "^1.1.1" + "@jsonjoy.com/util" "^1.1.2" + hyperdyperid "^1.2.0" + thingies "^1.20.0" + +"@jsonjoy.com/util@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@jsonjoy.com/util/-/util-1.1.3.tgz#75b1c3cf21b70e665789d1ad3eabeff8b7fd1429" + integrity sha512-g//kkF4kOwUjemValCtOc/xiYzmwMRmWq3Bn+YnzOzuZLHq2PpMOxxIayN3cKbo7Ko2Np65t6D9H81IvXbXhqg== + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" @@ -755,14 +787,19 @@ "@material/theme" "^14.0.0" tslib "^2.1.0" -"@material/web@1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@material/web/-/web-1.4.1.tgz#116480759d0b5815ea181086d32cc3ccb54a30f6" - integrity sha512-17MZA6Bt7ie6uDc5cVEGtm6frQfm7ASLGVJhJuYJ0W2Fs7Zogd8wJ3ExoYU8jyzv8LEXcfvnZKcvr8dlquGsig== +"@material/web@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@material/web/-/web-2.2.0.tgz#b98d57fc863301fe6c0036cc6f04869386067e08" + integrity sha512-k1Pi7f04qDRkHfIEdcnL++udN+x5Oq34R47n0Fk7Xo49uz9aKb6ZkO42jC6uvMH0ahOiFDktX9txHgYMDvNa+A== dependencies: lit "^2.7.4 || ^3.0.0" tslib "^2.4.0" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@socket.io/component-emitter@~3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" @@ -776,17 +813,17 @@ "@types/connect" "*" "@types/node" "*" -"@types/bonjour@^3.5.9": - version "3.5.10" - resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" - integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== +"@types/bonjour@^3.5.13": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== dependencies: "@types/node" "*" -"@types/connect-history-api-fallback@^1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" - integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== +"@types/connect-history-api-fallback@^1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== dependencies: "@types/express-serve-static-core" "*" "@types/node" "*" @@ -829,7 +866,7 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== -"@types/estree@^1.0.0": +"@types/estree@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== @@ -843,7 +880,17 @@ "@types/qs" "*" "@types/range-parser" "*" -"@types/express@*", "@types/express@^4.17.13": +"@types/express-serve-static-core@^4.17.33": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.1.tgz#57d34698bb580720fd6e3c360d4b2fdef579b979" + integrity sha512-ej0phymbFLoCB26dbbq5PGScsf2JAJ4IJHjG10LalgUV36XKTmA4GdA+PVllKvRk0sEKt64X8975qFnkSi0hqA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*": version "4.17.14" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg== @@ -853,6 +900,21 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/express@^4.17.21": + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + "@types/http-proxy@^1.17.8": version "1.17.9" resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a" @@ -870,6 +932,11 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + "@types/node-forge@^1.3.0": version "1.3.11" resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" @@ -892,19 +959,27 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== +"@types/retry@0.12.2": + version "0.12.2" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== -"@types/serve-index@^1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" - integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== dependencies: "@types/express" "*" -"@types/serve-static@*", "@types/serve-static@^1.13.10": +"@types/serve-static@*": version "1.15.0" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== @@ -912,10 +987,19 @@ "@types/mime" "*" "@types/node" "*" -"@types/sockjs@^0.3.33": - version "0.3.33" - resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" - integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== +"@types/serve-static@^1.15.5": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/sockjs@^0.3.36": + version "0.3.36" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== dependencies: "@types/node" "*" @@ -924,14 +1008,14 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== -"@types/ws@^8.5.1": - version "8.5.3" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" - integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== +"@types/ws@^8.5.10": + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== dependencies: "@types/node" "*" -"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.11.5": +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== @@ -997,7 +1081,7 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== -"@webassemblyjs/wasm-edit@^1.11.5": +"@webassemblyjs/wasm-edit@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== @@ -1032,7 +1116,7 @@ "@webassemblyjs/wasm-gen" "1.12.1" "@webassemblyjs/wasm-parser" "1.12.1" -"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.11.5": +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== @@ -1052,17 +1136,17 @@ "@webassemblyjs/ast" "1.12.1" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^2.1.0": +"@webpack-cli/configtest@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== -"@webpack-cli/info@^2.0.1": +"@webpack-cli/info@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== -"@webpack-cli/serve@^2.0.3": +"@webpack-cli/serve@^2.0.5": version "2.0.5" resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== @@ -1077,11 +1161,6 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abab@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -1090,10 +1169,10 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-import-assertions@^1.7.6: - version "1.8.0" - resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" - integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== acorn@^8.7.1: version "8.8.2" @@ -1117,7 +1196,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^5.0.0: +ajv-keywords@^5.0.0, ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== @@ -1144,6 +1223,16 @@ ajv@^8.0.0, ajv@^8.8.0: require-from-string "^2.0.2" uri-js "^4.2.2" +ajv@^8.9.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.13.0.tgz#a3939eaec9fb80d217ddf0c3376948c023f28c91" + integrity sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" @@ -1159,6 +1248,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -1166,6 +1260,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -1184,11 +1283,6 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== -array-flatten@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -1227,13 +1321,11 @@ body-parser@1.20.1, body-parser@^1.19.0: type-is "~1.6.18" unpipe "1.0.0" -bonjour-service@^1.0.11: - version "1.0.14" - resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.14.tgz#c346f5bc84e87802d08f8d5a60b93f758e514ee7" - integrity sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ== +bonjour-service@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" + integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== dependencies: - array-flatten "^2.1.2" - dns-equal "^1.0.0" fast-deep-equal "^3.1.3" multicast-dns "^7.2.5" @@ -1264,21 +1356,28 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.14.5: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== +browserslist@^4.21.10: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -1302,10 +1401,10 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001400: - version "1.0.30001435" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001435.tgz#502c93dbd2f493bee73a408fe98e98fb1dad10b2" - integrity sha512-kdCkUTjR+v4YAJelyiDTqiu82BDr4W4CP5sgTA0ZBmqn30XfS2ZghPLMowik9TPhS+psWJiUNxsqLyurDbmutA== +caniuse-lite@^1.0.30001587: + version "1.0.30001621" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001621.tgz#4adcb443c8b9c8303e04498318f987616b8fea2e" + integrity sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA== chalk@^4.1.0: version "4.1.2" @@ -1315,7 +1414,7 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1, chokidar@^3.5.3: +chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -1330,6 +1429,21 @@ chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" @@ -1460,7 +1574,7 @@ cors@~2.8.5: object-assign "^4" vary "^1" -cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1469,19 +1583,19 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -css-loader@6.7.3: - version "6.7.3" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.3.tgz#1e8799f3ccc5874fdd55461af51137fcc5befbcd" - integrity sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ== +css-loader@6.10.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.10.0.tgz#7c172b270ec7b833951b52c348861206b184a4b7" + integrity sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw== dependencies: icss-utils "^5.1.0" - postcss "^8.4.19" + postcss "^8.4.33" postcss-modules-extract-imports "^3.0.0" - postcss-modules-local-by-default "^4.0.0" - postcss-modules-scope "^3.0.0" + postcss-modules-local-by-default "^4.0.4" + postcss-modules-scope "^3.1.1" postcss-modules-values "^4.0.0" postcss-value-parser "^4.2.0" - semver "^7.3.8" + semver "^7.5.4" cssesc@^3.0.0: version "3.0.0" @@ -1505,7 +1619,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4.3.4, debug@^4.1.0, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: +debug@4.3.4, debug@^4.1.0, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2, debug@~4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1517,6 +1631,19 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" @@ -1524,10 +1651,10 @@ default-gateway@^6.0.3: dependencies: execa "^5.0.0" -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== depd@2.0.0: version "2.0.0" @@ -1559,11 +1686,6 @@ diff@5.0.0: resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== - dns-packet@^5.2.2: version "5.4.0" resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.4.0.tgz#1f88477cf9f27e78a213fb6d118ae38e759a879b" @@ -1581,35 +1703,45 @@ dom-serialize@^2.2.1: extend "^3.0.0" void-elements "^2.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.251: - version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" - integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +electron-to-chromium@^1.4.668: + version "1.4.780" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.780.tgz#8c6d7ff82b56b4219f2ae7918ee271ae25654f07" + integrity sha512-NPtACGFe7vunRYzvYqVRhQvsDrTevxpgDKxG/Vcbe0BTNOY+5+/2mOXSw2ls7ToNbE5Bf/+uQbjTxcmwMozpCw== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -engine.io-parser@~5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0" - integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== +engine.io-parser@~5.2.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.2.tgz#37b48e2d23116919a3453738c5720455e64e1c49" + integrity sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw== -engine.io@~6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.1.tgz#e3f7826ebc4140db9bbaa9021ad6b1efb175878f" - integrity sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA== +engine.io@~6.5.2: + version "6.5.4" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.4.tgz#6822debf324e781add2254e912f8568508850cdc" + integrity sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg== dependencies: "@types/cookie" "^0.4.1" "@types/cors" "^2.8.12" @@ -1619,13 +1751,13 @@ engine.io@~6.2.1: cookie "~0.4.1" cors "~2.8.5" debug "~4.3.1" - engine.io-parser "~5.0.3" - ws "~8.2.3" + engine.io-parser "~5.2.1" + ws "~8.11.0" -enhanced-resolve@^5.13.0: - version "5.16.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz#65ec88778083056cb32487faa9aef82ed0864787" - integrity sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA== +enhanced-resolve@^5.16.0: + version "5.16.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz#e8bc63d51b826d6f1cbc0a150ecb5a8b0c62e567" + integrity sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -1650,6 +1782,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -1843,6 +1980,14 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + format-util@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" @@ -1867,11 +2012,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-monkey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" - integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1923,17 +2063,27 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^5.0.1" once "^1.3.0" - path-is-absolute "^1.0.0" + +glob@^10.3.7: + version "10.3.16" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.16.tgz#bf6679d5d51279c8cfae4febe0d051d2a4bf4c6f" + integrity sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.1" + minipass "^7.0.4" + path-scurry "^1.11.0" glob@^7.1.3, glob@^7.1.7: version "7.2.3" @@ -1947,12 +2097,12 @@ glob@^7.1.3, glob@^7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -graceful-fs@^4.2.10: +graceful-fs@^4.2.10, graceful-fs@^4.2.11: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -2001,10 +2151,10 @@ hpack.js@^2.1.6: readable-stream "^2.0.1" wbuf "^1.1.0" -html-entities@^2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" - integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== +html-entities@^2.4.0: + version "2.5.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== http-deceiver@^1.2.7: version "1.2.7" @@ -2062,6 +2212,11 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +hyperdyperid@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" + integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2122,10 +2277,10 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" - integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== +ipaddr.js@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" + integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== is-binary-path@~2.1.0: version "2.1.0" @@ -2141,10 +2296,10 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.0" -is-docker@^2.0.0, is-docker@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== is-extglob@^2.1.1: version "2.1.1" @@ -2163,6 +2318,18 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-network-error@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-network-error/-/is-network-error-1.1.0.tgz#d26a760e3770226d11c169052f266a4803d9c997" + integrity sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -2195,12 +2362,12 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== dependencies: - is-docker "^2.0.0" + is-inside-container "^1.0.0" isarray@~1.0.0: version "1.0.0" @@ -2222,6 +2389,15 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +jackspeak@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.1.2.tgz#eada67ea949c6b71de50f1b09c92a961897b90ab" + integrity sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -2281,19 +2457,19 @@ karma-sourcemap-loader@0.4.0: dependencies: graceful-fs "^4.2.10" -karma-webpack@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.0.tgz#2a2c7b80163fe7ffd1010f83f5507f95ef39f840" - integrity sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA== +karma-webpack@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.1.tgz#4eafd31bbe684a747a6e8f3e4ad373e53979ced4" + integrity sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ== dependencies: glob "^7.1.3" - minimatch "^3.0.4" + minimatch "^9.0.3" webpack-merge "^4.1.5" -karma@6.4.2: - version "6.4.2" - resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.2.tgz#a983f874cee6f35990c4b2dcc3d274653714de8e" - integrity sha512-C6SU/53LB31BEgRg+omznBEMY4SjHU3ricV6zBcAe1EeILKkeScr+fZXtaI5WyDbkVowJxxAI6h73NcFPmXolQ== +karma@6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.3.tgz#763e500f99597218bbb536de1a14acc4ceea7ce8" + integrity sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q== dependencies: "@colors/colors" "1.5.0" body-parser "^1.19.0" @@ -2314,7 +2490,7 @@ karma@6.4.2: qjobs "^1.2.0" range-parser "^1.2.1" rimraf "^3.0.2" - socket.io "^4.4.1" + socket.io "^4.7.2" source-map "^0.6.1" tmp "^0.2.1" ua-parser-js "^0.7.30" @@ -2325,12 +2501,7 @@ kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -klona@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" - integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== - -launch-editor@^2.6.0: +launch-editor@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.1.tgz#f259c9ef95cbc9425620bbbd14b468fcdb4ffe3c" integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== @@ -2406,34 +2577,35 @@ log4js@^6.4.1: rfdc "^1.3.0" streamroller "^3.1.3" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" +lru-cache@^10.2.0: + version "10.2.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" + integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== material-icons@^1.13.12: version "1.13.12" resolved "https://registry.yarnpkg.com/material-icons/-/material-icons-1.13.12.tgz#eed4082bf0426642edeb027e75397e3064adc536" integrity sha512-/2YoaB79IjUK2B2JB+vIXXYGtBfHb/XG66LvoKVM5ykHW7yfrV5SP6d7KLX6iijY6/G9GqwgtPQ/sbhFnOURVA== -material-symbols@0.17.4: - version "0.17.4" - resolved "https://registry.yarnpkg.com/material-symbols/-/material-symbols-0.17.4.tgz#d44f9c47860487e74b2e9735425d49d99933ec01" - integrity sha512-5zI+rSzAidMJxAIrQCVwnp4rMjFnx8aQg68lfFXtaDeksZzJ7m8eDl16y9bRNxMosuYbLKeDHDbOWHPJJTSLhQ== +material-symbols@0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/material-symbols/-/material-symbols-0.25.1.tgz#e2d7b6cc1cf3eb54e7e0d223e0fe118d4fa7d308" + integrity sha512-0HopmXLjRs4H99LWajFWIXAt8DpaVMf9lyhKp35HQ+ocb7JJ3eXJTJNkOwccfbJ34qIuwYDwLJQtlzheMFmizw== media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== -memfs@^3.4.3: - version "3.4.12" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.12.tgz#d00f8ad8dab132dc277c659dc85bfd14b07d03bd" - integrity sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw== +memfs@^4.6.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.9.2.tgz#42e7b48207268dad8c9c48ea5d4952c5d3840433" + integrity sha512-f16coDZlTG1jskq3mxarwB+fGRrd0uXWt+o1WIhRfOwbXQZqUDsTVxQBFK9JjRQHblg8eAG2JSbprDXKjc7ijQ== dependencies: - fs-monkey "^1.0.3" + "@jsonjoy.com/json-pack" "^1.0.3" + "@jsonjoy.com/util" "^1.1.2" + sonic-forest "^1.0.0" + tslib "^2.0.0" merge-descriptors@1.0.1: version "1.0.1" @@ -2504,11 +2676,30 @@ minimatch@^3.0.4, minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.1, minimatch@^9.0.3: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.3, minimist@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4: + version "7.1.1" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.1.tgz#f7f85aff59aa22f110b20e27692465cf3bf89481" + integrity sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA== + mkdirp@^0.5.5: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -2516,10 +2707,10 @@ mkdirp@^0.5.5: dependencies: minimist "^1.2.6" -mocha@10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" - integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== +mocha@10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.3.0.tgz#0e185c49e6dccf582035c05fa91084a4ff6e3fe9" + integrity sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg== dependencies: ansi-colors "4.1.1" browser-stdout "1.3.1" @@ -2528,13 +2719,12 @@ mocha@10.2.0: diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.2.0" + glob "8.1.0" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" minimatch "5.0.1" ms "2.1.3" - nanoid "3.3.3" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" @@ -2566,11 +2756,6 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" -nanoid@3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" - integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== - nanoid@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" @@ -2591,10 +2776,10 @@ node-forge@^1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" @@ -2623,7 +2808,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@2.4.1: +on-finished@2.4.1, on-finished@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -2656,14 +2841,15 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@^8.0.9: - version "8.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" - integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== +open@^10.0.3: + version "10.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" p-limit@^2.2.0: version "2.3.0" @@ -2693,12 +2879,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-retry@^4.5.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== +p-retry@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.0.tgz#8d6df01af298750009691ce2f9b3ad2d5968f3bd" + integrity sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA== dependencies: - "@types/retry" "0.12.0" + "@types/retry" "0.12.2" + is-network-error "^1.0.0" retry "^0.13.1" p-try@^2.0.0: @@ -2731,6 +2918,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.11.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -2741,6 +2936,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -2758,19 +2958,19 @@ postcss-modules-extract-imports@^3.0.0: resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== -postcss-modules-local-by-default@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" - integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== +postcss-modules-local-by-default@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz#f1b9bd757a8edf4d8556e8d0f4f894260e3df78f" + integrity sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw== dependencies: icss-utils "^5.0.0" postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" -postcss-modules-scope@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" - integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== +postcss-modules-scope@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz#a43d28289a169ce2c15c00c4e64c0858e43457d5" + integrity sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ== dependencies: postcss-selector-parser "^6.0.4" @@ -2794,14 +2994,14 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.4.19: - version "8.4.35" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.35.tgz#60997775689ce09011edf083a549cea44aabe2f7" - integrity sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA== +postcss@^8.4.33: + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== dependencies: nanoid "^3.3.7" picocolors "^1.0.0" - source-map-js "^1.0.2" + source-map-js "^1.2.0" process-nextick-args@~2.0.0: version "2.0.1" @@ -2944,6 +3144,18 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@^5.0.5: + version "5.0.7" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.7.tgz#27bddf202e7d89cb2e0381656380d1734a854a74" + integrity sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg== + dependencies: + glob "^10.3.7" + +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -2959,18 +3171,17 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass-loader@13.2.2: - version "13.2.2" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-13.2.2.tgz#f97e803993b24012c10d7ba9676548bf7a6b18b9" - integrity sha512-nrIdVAAte3B9icfBiGWvmMhT/D+eCDwnk+yA7VE/76dp/WkHX+i44Q/pfo71NYbwj0Ap+PGsn0ekOuU1WFJ2AA== +sass-loader@14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-14.1.1.tgz#2c9d2277c5b1c5fe789cd0570c046d8ad23cb7ca" + integrity sha512-QX8AasDg75monlybel38BZ49JP5Z+uSKfKwF2rO7S74BywaRmGQMUBw9dtkS+ekyM/QnP+NOrRYq8ABMZ9G8jw== dependencies: - klona "^2.0.6" neo-async "^2.6.2" -sass@1.62.1: - version "1.62.1" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.62.1.tgz#caa8d6bf098935bc92fc73fa169fb3790cacd029" - integrity sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A== +sass@1.72.0: + version "1.72.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.72.0.tgz#5b9978943fcfb32b25a6a5acb102fc9dabbbf41c" + integrity sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA== dependencies: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" @@ -2985,7 +3196,7 @@ schema-utils@^3.1.1: ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^3.1.2: +schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -3004,12 +3215,22 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.0.0" +schema-utils@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== -selfsigned@^2.1.1: +selfsigned@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== @@ -3017,12 +3238,10 @@ selfsigned@^2.1.1: "@types/node-forge" "^1.3.0" node-forge "^1" -semver@^7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== - dependencies: - lru-cache "^6.0.0" +semver@^7.5.4: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== send@0.18.0: version "0.18.0" @@ -3128,30 +3347,39 @@ signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -socket.io-adapter@~2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" - integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +socket.io-adapter@~2.5.2: + version "2.5.4" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz#4fdb1358667f6d68f25343353bd99bd11ee41006" + integrity sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg== + dependencies: + debug "~4.3.4" + ws "~8.11.0" -socket.io-parser@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5" - integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g== +socket.io-parser@~4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" + integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" -socket.io@^4.4.1: - version "4.5.4" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.4.tgz#a4513f06e87451c17013b8d13fdfaf8da5a86a90" - integrity sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ== +socket.io@^4.7.2: + version "4.7.5" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.5.tgz#56eb2d976aef9d1445f373a62d781a41c7add8f8" + integrity sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA== dependencies: accepts "~1.3.4" base64id "~2.0.0" + cors "~2.8.5" debug "~4.3.2" - engine.io "~6.2.1" - socket.io-adapter "~2.4.0" - socket.io-parser "~4.2.1" + engine.io "~6.5.2" + socket.io-adapter "~2.5.2" + socket.io-parser "~4.2.4" sockjs@^0.3.24: version "0.3.24" @@ -3162,17 +3390,28 @@ sockjs@^0.3.24: uuid "^8.3.2" websocket-driver "^0.7.4" +sonic-forest@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sonic-forest/-/sonic-forest-1.0.3.tgz#81363af60017daba39b794fce24627dc412563cb" + integrity sha512-dtwajos6IWMEWXdEbW1IkEkyL2gztCAgDplRIX+OT5aRKnEd5e7r7YCxRgXZdhRP1FBdOBf8axeTPhzDv8T4wQ== + dependencies: + tree-dump "^1.0.0" + "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-loader@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-4.0.1.tgz#72f00d05f5d1f90f80974eda781cbd7107c125f2" - integrity sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA== +source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + +source-map-loader@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-5.0.0.tgz#f593a916e1cc54471cfc8851b905c8a845fc7e38" + integrity sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA== dependencies: - abab "^2.0.6" iconv-lite "^0.6.3" source-map-js "^1.0.2" @@ -3231,7 +3470,7 @@ streamroller@^3.1.3: debug "^4.3.4" fs-extra "^8.1.0" -string-width@^4.1.0, string-width@^4.2.0: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -3240,6 +3479,15 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -3254,13 +3502,20 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -3271,10 +3526,10 @@ strip-json-comments@3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -style-loader@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.2.tgz#eaebca714d9e462c19aa1e3599057bc363924899" - integrity sha512-RHs/vcrKdQK8wZliteNK4NKzxvLBzpuHMqYmUVWeKa6MkaIQ97ZTOS0b+zapZhy6GcrgWnvWYCMHRirC3FsUmw== +style-loader@3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" + integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== supports-color@8.1.1, supports-color@^8.0.0: version "8.1.1" @@ -3300,7 +3555,7 @@ tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.3.7: +terser-webpack-plugin@^5.3.10: version "5.3.10" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== @@ -3321,6 +3576,11 @@ terser@^5.26.0: commander "^2.20.0" source-map-support "~0.5.20" +thingies@^1.20.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/thingies/-/thingies-1.21.0.tgz#e80fbe58fd6fdaaab8fad9b67bd0a5c943c445c1" + integrity sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g== + thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" @@ -3345,16 +3605,21 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -tslib@^2.1.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +tree-dump@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tree-dump/-/tree-dump-1.0.1.tgz#b448758da7495580e6b7830d6b7834fca4c45b96" + integrity sha512-WCkcRBVPSlHHq1dc/px9iOfqklvzCbdRwvlNfxGZsrHqf6aZttfPrd7DJTt6oR10dwUfpFFQeVTkPbBIZxX/YA== -tslib@^2.4.0: +tslib@^2.0.0, tslib@^2.4.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@^2.1.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -3363,10 +3628,10 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typescript@5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" - integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== +typescript@5.4.3: + version "5.4.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff" + integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg== ua-parser-js@^0.7.30: version "0.7.32" @@ -3383,15 +3648,15 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -update-browserslist-db@^1.0.9: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== +update-browserslist-db@^1.0.13: + version "1.0.16" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz#f6d489ed90fb2f07d67784eb3f53d7891f736356" + integrity sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ== dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" + escalade "^3.1.2" + picocolors "^1.0.1" -uri-js@^4.2.2: +uri-js@^4.2.2, uri-js@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== @@ -3423,10 +3688,10 @@ void-elements@^2.0.0: resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== -watchpack@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== +watchpack@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff" + integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -3438,15 +3703,15 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -webpack-cli@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.0.tgz#abc4b1f44b50250f2632d8b8b536cfe2f6257891" - integrity sha512-a7KRJnCxejFoDpYTOwzm5o21ZXMaNqtRlvS183XzGDUPRdVEzJNImcQokqYZ8BNTnk9DkKiuWxw75+DCCoZ26w== +webpack-cli@5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^2.1.0" - "@webpack-cli/info" "^2.0.1" - "@webpack-cli/serve" "^2.0.3" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" colorette "^2.0.14" commander "^10.0.1" cross-spawn "^7.0.3" @@ -3457,52 +3722,53 @@ webpack-cli@5.1.0: rechoir "^0.8.0" webpack-merge "^5.7.3" -webpack-dev-middleware@^5.3.1: - version "5.3.3" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" - integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== +webpack-dev-middleware@^7.1.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-7.2.1.tgz#2af00538b6e4eda05f5afdd5d711dbebc05958f7" + integrity sha512-hRLz+jPQXo999Nx9fXVdKlg/aehsw1ajA9skAneGmT03xwmyuhvF93p6HUKKbWhXdcERtGTzUCtIQr+2IQegrA== dependencies: colorette "^2.0.10" - memfs "^3.4.3" + memfs "^4.6.0" mime-types "^2.1.31" + on-finished "^2.4.1" range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@4.15.0: - version "4.15.0" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz#87ba9006eca53c551607ea0d663f4ae88be7af21" - integrity sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ== - dependencies: - "@types/bonjour" "^3.5.9" - "@types/connect-history-api-fallback" "^1.3.5" - "@types/express" "^4.17.13" - "@types/serve-index" "^1.9.1" - "@types/serve-static" "^1.13.10" - "@types/sockjs" "^0.3.33" - "@types/ws" "^8.5.1" +webpack-dev-server@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz#cb6ea47ff796b9251ec49a94f24a425e12e3c9b8" + integrity sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA== + dependencies: + "@types/bonjour" "^3.5.13" + "@types/connect-history-api-fallback" "^1.5.4" + "@types/express" "^4.17.21" + "@types/serve-index" "^1.9.4" + "@types/serve-static" "^1.15.5" + "@types/sockjs" "^0.3.36" + "@types/ws" "^8.5.10" ansi-html-community "^0.0.8" - bonjour-service "^1.0.11" - chokidar "^3.5.3" + bonjour-service "^1.2.1" + chokidar "^3.6.0" colorette "^2.0.10" compression "^1.7.4" connect-history-api-fallback "^2.0.0" default-gateway "^6.0.3" express "^4.17.3" graceful-fs "^4.2.6" - html-entities "^2.3.2" + html-entities "^2.4.0" http-proxy-middleware "^2.0.3" - ipaddr.js "^2.0.1" - launch-editor "^2.6.0" - open "^8.0.9" - p-retry "^4.5.0" - rimraf "^3.0.2" - schema-utils "^4.0.0" - selfsigned "^2.1.1" + ipaddr.js "^2.1.0" + launch-editor "^2.6.1" + open "^10.0.3" + p-retry "^6.2.0" + rimraf "^5.0.5" + schema-utils "^4.2.0" + selfsigned "^2.4.1" serve-index "^1.9.1" sockjs "^0.3.24" spdy "^4.0.2" - webpack-dev-middleware "^5.3.1" - ws "^8.13.0" + webpack-dev-middleware "^7.1.0" + ws "^8.16.0" webpack-merge@^4.1.5: version "4.2.2" @@ -3524,34 +3790,34 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@5.82.0: - version "5.82.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.82.0.tgz#3c0d074dec79401db026b4ba0fb23d6333f88e7d" - integrity sha512-iGNA2fHhnDcV1bONdUu554eZx+XeldsaeQ8T67H6KKHl2nUSwX8Zm7cmzOA46ox/X1ARxf7Bjv8wQ/HsB5fxBg== +webpack@5.91.0: + version "5.91.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.91.0.tgz#ffa92c1c618d18c878f06892bbdc3373c71a01d9" + integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw== dependencies: "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.0" - "@webassemblyjs/ast" "^1.11.5" - "@webassemblyjs/wasm-edit" "^1.11.5" - "@webassemblyjs/wasm-parser" "^1.11.5" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" acorn "^8.7.1" - acorn-import-assertions "^1.7.6" - browserslist "^4.14.5" + acorn-import-assertions "^1.9.0" + browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.13.0" + enhanced-resolve "^5.16.0" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.2" + schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.7" - watchpack "^2.4.0" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" webpack-sources "^3.2.3" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: @@ -3592,7 +3858,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -3601,31 +3867,35 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@^8.13.0: - version "8.16.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" - integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== +ws@^8.16.0: + version "8.17.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" + integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== -ws@~8.2.3: - version "8.2.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" - integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== +ws@~8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" diff --git a/settings.gradle.kts b/settings.gradle.kts index 128fa6fe..c5dfdfa9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,25 +6,29 @@ project(":compose-multiplatform-common:legacy").name = "compose-multiplatform-co include("compose-multiplatform-material-icons-core") include("compose-multiplatform-material2") include("compose-multiplatform-material3") +include("compose-multiplatform-navigation") +include("compose-multiplatform-lifecycle-viewmodel") include("demo") +/* +// This seems not needed. pluginManagement { repositories { //mavenLocal() gradlePluginPortal() google() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") mavenCentral() } } +*/ +// This is needed for Kotlin Native. dependencyResolutionManagement { @Suppress("UnstableApiUsage") repositories { mavenLocal() mavenCentral() google() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } }