diff --git a/LEGACY_README.md b/LEGACY_README.md new file mode 100644 index 00000000..ba3722d8 --- /dev/null +++ b/LEGACY_README.md @@ -0,0 +1,18 @@ +# Legacy README + +This file contains information on legacy code which is not removed yet. + +## Supported features + +### Styles + +The `ModifierOrAttrsScope.styles` function and the `StyleScope` class provide a universal interface for `Modifier`s and CSS styles. + +The functions in `StyleScope`: + +- `height` +- `margin` +- `width` +- `backgroundColor` +- `platformBorder` +- `outerBorder` diff --git a/README.md b/README.md index 4e38e401..611153e1 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,21 @@ -# Compose Multiplatform Material wrappers for `androidx.compose.material` and Compose HTML +# Compose Multiplatform Material: 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-material)](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-material) +[![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) -Some simple unified Compose Multiplatform wrappers of common components, layouts, and Material Design components for `androidx.compose.material` (officially supported on Android, desktop (JVM), iOS, web (Kotlin/Wasm)) and Compose HTML (mainly based on [Kobweb Silk](https://github.com/varabyte/kobweb?tab=readme-ov-file#silk) and [KMDC](https://github.com/mpetuska/kmdc)) +Unified Compose Multiplatform wrappers of common components, layouts, and Material Design components for `androidx.compose` (officially supported on Android, desktop (JVM), iOS, and web (Kotlin/Wasm)) and Compose HTML (mainly based on [Kobweb Silk](https://github.com/varabyte/kobweb?tab=readme-ov-file#silk) [Compose](https://github.com/varabyte/kobweb/tree/main/frontend/kobweb-compose), [KMDC](https://github.com/mpetuska/kmdc), and [Compose HTML Material](https://github.com/huanshankeji/compose-html-material) (which is then based on [Material Web](https://github.com/material-components/material-web))) - +We try to provide a set of common extensions and composable component APIs akin to those in `androidx.compose` (`androidx.compose.foundation`, `androidx.compose.material`, and `androidx.compose.material3`), meanwhile making them compatible with the Compose HTML APIs. However, only subsets of the composables and composable parameters are supported due to the API differences, limitations of the JS (web) platform and the Compose HTML composables this project depends on, and our limited effort. -We try to make the function types of the composable components follow those in `androidx.compose.foundation` and `androidx.compose.material`, meanwhile being compatible with the Compose HTML APIs. However, only subsets of the composables and composable arguments are supported due to the API differences, limitations of the Compose HTML composables this project depends on, and our limited effort. +Complete visual consistency across different platforms is not guaranteed. -Visual consistency across different platforms is not guaranteed. - -This project is prototype and there is no documentation yet. Check out [the demo project](demo) on how to use the components. +This project is still in development and has not reached the stable state yet. Some APIs are likely to be changed and there is no detailed documentation yet. Check out [the demo project](demo) on how to use the components in addition to the information below. ## Supported features ### Components -#### Common (Foundation) components +#### Foundation components - `BasicText` @@ -28,46 +26,80 @@ This project is prototype and there is no documentation yet. Check out [the demo ##### Layouts - `Box` -- `Column` (via flexbox on web) -- `Row` (via flexbox on web) +- `Column` (via flexbox on JS) +- `Row` (via flexbox on JS) +- `Spacer` + +##### Lazy + +- `LazyColumn` +- `LazyRow` -#### Material components +#### Material 2 components - `Button` - `Card` +- `Checkbox` +- `Divider` (not working properly on JS yet) - `Icon` - `IconButton` -- `ScrollableList`/`LazyColumn` (visually inconsistent for now) -- `Text`/`MaterialText` -- `TextField` +- `Switch` +- `Text` + +##### `ext` components + +- `Button` +- `IconButton` +- `RadioRow`, `RadioGroupRow` +- `SwitchWithLabel` +- `MaterialText`, `InlineText` +- `TextField`, `OutlinedTextField` - `TopAppBarScaffold` -#### Components in the `ext` packages +##### `lazy.ext` components -The components in the `ext` packages don't follow the `androidx.compose` APIs exactly, but rather provide wrappers are idiomatic and conventional on both kinds of targets, wrapping different APIs which can't be unified following the `androidx.compose` APIs. +- `List`/`LazyColumnList` (visually inconsistent for now) -### Styles (obsolete in the legacy module, for removal) +#### Material 3 components -The `ModifierOrAttrsScope.styles` function and the `StyleScope` class provide a universal interface for `Modifier`s and CSS styles. +- `Button` (`FilledButton`), `ElevatedButton`, `FilledTonalButton`, `OutlinedButton`, `TextButton` +- `Card` (`FilledCard`), `ElevatedCard`, `OutlinedCard` +- `Checkbox` +- `FloatingActionButton`, `SmallFloatingActionButton`, `LargeFloatingActionButton`, `ExtendedFloatingActionButton` +- `Icon` +- `IconButton`, `IconToggleButton`, `FilledIconButton`, `FilledIconToggleButton`, `FilledTonalIconButton`, `FilledTonalIconToggleButton`, `OutlinedIconButton`, `OutlinedIconToggleButton` +- `Switch` +- `Text` -The functions in `StyleScope`: +##### `ext` components -- `height` -- `margin` -- `width` -- `backgroundColor` -- `platformBorder` -- `outerBorder` +- `Button` (`FilledButton`), `ElevatedButton`, `FilledTonalButton`, `OutlinedButton`, `TextButton` +- `Card` (`FilledCard`), `ElevatedCard`, `OutlinedCard` +- `FloatingActionButton`, `SmallFloatingActionButton`, `LargeFloatingActionButton`, `ExtendedFloatingActionButton` +- `IconButton`, `IconToggleButton`, `FilledIconButton`, `FilledIconToggleButton`, `FilledTonalIconButton`, `FilledTonalIconToggleButton`, `OutlinedIconButton`, `OutlinedIconToggleButton` +- `NavigationBar`, `NavigationBarItem` +- `MaterialText`, `InlineText` +- `TextField`, `OutlinedTextField` + +##### `lazy.ext` components + +- `List`/`LazyColumnList` (slightly visually inconsistent) + +#### About `ext` components (components in the `ext` packages) + +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. ### Modifiers - size modifiers - - `size`, `sizeIn`, `fillMaxSize` - - `width`, `widthIn`, `fillMaxWidth` - - `height`, `heightIn`, `fillMaxHeight` + - `size`, `sizeIn`, `fillMaxSize` + - `width`, `widthIn`, `fillMaxWidth` + - `height`, `heightIn`, `fillMaxHeight` - `padding` - `background` - `border` (visually inconsistent) +- `onClick` +- `verticalScroll`, `horizontalScroll` (`ScrollState` not supported on JS yet) #### `ext` modifiers @@ -82,22 +114,42 @@ Maven coordinate: "com.huanshankeji:compose-multiplatform-$module:$version" ``` +For example, depend on the Material 3 module with Gradle: + +```kotlin +kotlin { + sourceSets { + commonMain { + dependencies { + // ... + implementation("com.huanshankeji:compose-multiplatform-material3:$version") + } + } + } +} +``` + View [all the artifacts on Maven Central](https://search.maven.org/search?q=g:com.huanshankeji%20AND%20a:compose-multiplatform-*). This project depends on [Kobweb](https://github.com/varabyte/kobweb) which is not published to Maven Central yet, so you have to add the following Maven repository: ```kotlin repositories { + mavenCentral() maven("https://us-central1-maven.pkg.dev/varabyte-repos/public") } ``` +### Material Symbols & Icons on JS + +See [the corresponding section in Compose HTML Material](https://github.com/huanshankeji/compose-html-material?tab=readme-ov-file#material-symbols--icons) for configuring Material Icons on JS. + ## About Kobweb Silk -The Kotlin/JS (Compose HTML) portion of this project depends on [Kobweb Compose](https://github.com/varabyte/kobweb/blob/main/frontend/kobweb-compose/README.md) of [Kobweb Silk](https://github.com/varabyte/kobweb?tab=readme-ov-file#silk) which is a UI layer built upon Compose HTML that provides `Modifier` APIs (type-safe CSS API wrappers) and layouts. Here is a list of topics in their README.md that should be helpful when you use this library in Compose HTML: +The Kotlin/JS (Compose HTML) portion of this project depends on [Kobweb Compose](https://github.com/varabyte/kobweb/blob/main/frontend/kobweb-compose/README.md) of [Kobweb Silk](https://github.com/varabyte/kobweb?tab=readme-ov-file#silk) which is a UI layer built upon Compose HTML that provides `Modifier` (type-safe CSS API wrappers) and layout APIs. Here is a list of topics in their README.md that should be helpful when you use this library in Compose HTML, especially if you need to customize the components further on Kotlin/JS (Compose HTML): 1. [Silk](https://github.com/varabyte/kobweb?tab=readme-ov-file#silk) - 1. [Modifier](https://github.com/varabyte/kobweb?tab=readme-ov-file#modifier) - 1. [attrsModifier and styleModifier](https://github.com/varabyte/kobweb?tab=readme-ov-file#attrsmodifier-and-stylemodifier) + 1. [Modifier](https://github.com/varabyte/kobweb?tab=readme-ov-file#modifier) + 1. [attrsModifier and styleModifier](https://github.com/varabyte/kobweb?tab=readme-ov-file#attrsmodifier-and-stylemodifier) 1. [General purpose improvements on top of Compose HTML and Kotlin/JS](https://github.com/varabyte/kobweb?tab=readme-ov-file#general-purpose-improvements-on-top-of-compose-html-and-kotlinjs) 1. [What about Compose Multiplatform for Web?](https://github.com/varabyte/kobweb?tab=readme-ov-file#what-about-compose-multiplatform-for-web) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index ef8ab1ac..4536c58c 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -12,7 +12,7 @@ repositories { dependencies { implementation(kotlin("gradle-plugin", "1.9.23")) - implementation("org.jetbrains.compose:compose-gradle-plugin:1.6.1") + 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") } diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index b1149234..54ab91fa 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -1,14 +1,16 @@ -val projectVersion = "0.2.0" +import org.jetbrains.compose.ComposeBuildConfig + +val projectVersion = "0.3.0" object DependencyVersions { - val composeMultiplatform = "1.6.1" // manually specified for "ui-unit" - val kobweb = "0.17.1" - val huanshankejiComposeWeb = "0.2.2" + const val composeMultiplatform = ComposeBuildConfig.composeVersion // for "ui-unit" + val kobweb = "0.17.3" + val huanshankejiComposeHtml = "0.3.0" val kmdc = "0.1.2" - val materialIcons = "1.13.12" + val materialSymbols = "0.17.4" object Androidx { - val activityCompose = "1.8.2" - val compose = "1.6.4" + val activityCompose = "1.9.0" + val compose = "1.6.6" } } diff --git a/buildSrc/src/main/kotlin/common-conventions.gradle.kts b/buildSrc/src/main/kotlin/common-conventions.gradle.kts index 9f468689..33fca5d7 100644 --- a/buildSrc/src/main/kotlin/common-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/common-conventions.gradle.kts @@ -1,3 +1,4 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl plugins { @@ -35,4 +36,10 @@ kotlin { // for JS and HTML wrappers js() + + + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } } diff --git a/compose-multiplatform-common/build.gradle.kts b/compose-multiplatform-common/build.gradle.kts index 885eb0c2..1c94940d 100644 --- a/compose-multiplatform-common/build.gradle.kts +++ b/compose-multiplatform-common/build.gradle.kts @@ -38,6 +38,7 @@ kotlin { api(compose.html.core) // see: https://github.com/varabyte/kobweb/blob/main/frontend/kobweb-compose/build.gradle.kts api("com.varabyte.kobweb:kobweb-compose:${DependencyVersions.kobweb}") + implementation("com.huanshankeji:compose-html-common:${DependencyVersions.huanshankejiComposeHtml}") } } } diff --git a/compose-multiplatform-common/legacy/build.gradle.kts b/compose-multiplatform-common/legacy/build.gradle.kts index 4bef7951..8f0df908 100644 --- a/compose-multiplatform-common/legacy/build.gradle.kts +++ b/compose-multiplatform-common/legacy/build.gradle.kts @@ -22,7 +22,7 @@ kotlin { dependencies { implementation(compose.html.core) - api("com.huanshankeji:compose-web-common:${DependencyVersions.huanshankejiComposeWeb}") + api("com.huanshankeji:compose-html-common:${DependencyVersions.huanshankejiComposeHtml}") } } } @@ -33,7 +33,7 @@ publishing.publications.withType { project, "Legacy Compose Multiplatform common wrappers", "Legacy common wrappers of components (including layouts) and styles for Compose Multiplatform on (desktop/Android and web)\n" + - "This legacy module depends on `com.huanshankeji:compose-web-common` instead of Kobweb Silk " + + "This legacy module depends on `com.huanshankeji:compose-html-common` instead of Kobweb Silk " + "and its components use the `ModifierOrAttrsScope` class to configure styles." ) { `Shreck Ye`() diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/Content.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/Content.androidxCommon.kt new file mode 100644 index 00000000..ef41ed0c --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/Content.androidxCommon.kt @@ -0,0 +1,10 @@ +package com.huanshankeji.compose + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +fun (@Composable (Modifier) -> Unit).toContentWithoutModifier(): @Composable () -> Unit = + { this(Modifier) } + +fun (@Composable (Modifier) -> Unit)?.toNullableContentWithoutModifier(): @Composable (() -> Unit)? = + this?.toContentWithoutModifier() 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 new file mode 100644 index 00000000..0ccd6995 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/OnClick.androidxCommon.kt @@ -0,0 +1,9 @@ +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. +@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/Scroll.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.androidxCommon.kt new file mode 100644 index 00000000..068fe97e --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.androidxCommon.kt @@ -0,0 +1,19 @@ +package com.huanshankeji.compose.foundation + +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun rememberScrollState(initial: Int): ScrollState = + rememberScrollState(initial) + +actual typealias ScrollState = androidx.compose.foundation.ScrollState + +actual fun Modifier.verticalScroll(state: ScrollState): Modifier = + platformModify { verticalScroll(state) } + +actual fun Modifier.horizontalScroll(state: ScrollState): Modifier = + platformModify { horizontalScroll(state) } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.androidxCommon.kt index 0e4c6e61..fd186ea2 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.androidxCommon.kt @@ -1,11 +1,11 @@ package com.huanshankeji.compose.foundation.layout +import androidx.annotation.FloatRange import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier import kotlin.jvm.JvmInline -import androidx.compose.foundation.layout.ColumnScope as PlatformColumnScope @Composable actual fun Column( @@ -18,14 +18,25 @@ actual fun Column( modifier.platformModifier, verticalArrangement.platformValue, horizontalAlignment.platformHorizontal, - ) { ColumnScope.Impl(this).content() } + content.toCommonColumnScopeContent() + ) + + +actual typealias PlatformColumnScope = androidx.compose.foundation.layout.ColumnScope //@LayoutScopeMarker actual interface ColumnScope { - val platformValue: PlatformColumnScope + actual val platformValue: PlatformColumnScope @JvmInline - value class Impl(override val platformValue: PlatformColumnScope) : ColumnScope + actual value class Impl(override val platformValue: PlatformColumnScope) : ColumnScope + + @Stable + actual fun Modifier.weight( + @FloatRange(from = 0.0, fromInclusive = false) + weight: Float + ): Modifier = + with(platformValue) { platformModify { weight(weight) } } @Stable actual fun Modifier.align(alignment: Alignment.Horizontal): Modifier = 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 b26d0074..56babac8 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 @@ -1,11 +1,11 @@ package com.huanshankeji.compose.foundation.layout +import androidx.annotation.FloatRange import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier import kotlin.jvm.JvmInline -import androidx.compose.foundation.layout.RowScope as PlatformRowScope @Composable actual fun Row( @@ -18,14 +18,24 @@ actual fun Row( modifier.platformModifier, horizontalArrangement.platformValue, verticalAlignment.platformHorizontal, - ) { RowScope.Impl(this).content() } + content.toPlatformRowScopeContent(), + ) + + +actual typealias PlatformRowScope = androidx.compose.foundation.layout.RowScope //@LayoutScopeMarker actual interface RowScope { - val platformValue: PlatformRowScope + actual val platformValue: PlatformRowScope @JvmInline - value class Impl(override val platformValue: PlatformRowScope) : RowScope + actual value class Impl(override val platformValue: PlatformRowScope) : RowScope + + actual fun Modifier.weight( + @FloatRange(from = 0.0, fromInclusive = false) + weight: Float + ): Modifier = + with(platformValue) { platformModify { weight(weight) } } @Stable actual fun Modifier.align(alignment: Alignment.Vertical): Modifier = diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Spacer.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Spacer.androidxCommon.kt new file mode 100644 index 00000000..f762f66c --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/layout/Spacer.androidxCommon.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.foundation.layout + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Spacer(modifier: Modifier) = + androidx.compose.foundation.layout.Spacer(modifier.platformModifier) 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 new file mode 100644 index 00000000..0047c1ba --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.androidxCommon.kt @@ -0,0 +1,57 @@ +package com.huanshankeji.compose.foundation.lazy + +import androidx.compose.foundation.lazy.LazyScopeMarker +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.Arrangement +import com.huanshankeji.compose.ui.Alignment +import com.huanshankeji.compose.ui.Modifier + +@LazyScopeMarker +//@JvmDefaultWithCompatibility +actual class LazyListScope(val platformValue: androidx.compose.foundation.lazy.LazyListScope) { + actual fun item( + key: Any?, + contentType: Any?, + content: @Composable LazyItemScope.() -> Unit + ) = + platformValue.item(key, contentType) { LazyItemScope(this).content() } + + + actual fun items( + count: Int, + key: ((index: Int) -> Any)?, + contentType: (index: Int) -> Any?, + itemContent: @Composable LazyItemScope.(index: Int) -> Unit + ) = + platformValue.items(count, key, contentType) { index -> LazyItemScope(this).itemContent(index) } +} + +@Composable +actual fun LazyRow( + modifier: Modifier, + reverseLayout: Boolean, + horizontalArrangement: Arrangement.Horizontal, + verticalAlignment: Alignment.Vertical, + content: LazyListScope.() -> Unit +) = + androidx.compose.foundation.lazy.LazyRow( + modifier.platformModifier, + reverseLayout = reverseLayout, + horizontalArrangement = horizontalArrangement.platformValue, + verticalAlignment = verticalAlignment.platformHorizontal + ) { LazyListScope(this).content() } + +@Composable +actual fun LazyColumn( + modifier: Modifier, + reverseLayout: Boolean, + verticalArrangement: Arrangement.Vertical, + horizontalAlignment: Alignment.Horizontal, + content: LazyListScope.() -> Unit +) = + androidx.compose.foundation.lazy.LazyColumn( + modifier.platformModifier, + reverseLayout = reverseLayout, + verticalArrangement = verticalArrangement.platformValue, + horizontalAlignment = horizontalAlignment.platformHorizontal + ) { LazyListScope(this).content() } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.androidxCommon.kt new file mode 100644 index 00000000..c6c00935 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.androidxCommon.kt @@ -0,0 +1,30 @@ +package com.huanshankeji.compose.foundation.lazy + +import androidx.annotation.FloatRange +import androidx.compose.foundation.lazy.LazyScopeMarker +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier +import androidx.compose.foundation.lazy.LazyItemScope as PlatformLazyItemScope + +@Stable +@LazyScopeMarker +//@JvmDefaultWithCompatibility +actual class LazyItemScope(val platformValue: PlatformLazyItemScope) { + actual fun Modifier.fillParentMaxSize( + @FloatRange(from = 0.0, to = 1.0) + fraction: Float + ): Modifier = + platformModify { with(platformValue) { fillParentMaxSize(fraction) } } + + actual fun Modifier.fillParentMaxWidth( + @FloatRange(from = 0.0, to = 1.0) + fraction: Float + ): Modifier = + platformModify { with(platformValue) { fillParentMaxWidth(fraction) } } + + actual fun Modifier.fillParentMaxHeight( + @FloatRange(from = 0.0, to = 1.0) + fraction: Float + ): Modifier = + platformModify { with(platformValue) { fillParentMaxHeight(fraction) } } +} \ No newline at end of file diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardActions.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardActions.androidxCommon.kt new file mode 100644 index 00000000..51ebf8e4 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardActions.androidxCommon.kt @@ -0,0 +1,19 @@ +package com.huanshankeji.compose.foundation.text + +import com.huanshankeji.compose.foundation.text.input.ImeAction +import com.huanshankeji.compose.foundation.text.input.toPlatformValue +import androidx.compose.foundation.text.KeyboardActionScope as PlatformKeyboardActionScope +import androidx.compose.foundation.text.KeyboardActions as PlatformKeyboardActions + +fun KeyboardActions.toPlatformValue(): PlatformKeyboardActions = + onAny?.let { + PlatformKeyboardActions { + toCommonValue().it() + } + } ?: PlatformKeyboardActions() + +fun PlatformKeyboardActionScope.toCommonValue(): KeyboardActionScope = + object : KeyboardActionScope { + override fun defaultKeyboardAction(imeAction: ImeAction) = + this@toCommonValue.defaultKeyboardAction(imeAction.toPlatformValue()) + } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptions.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptions.androidxCommon.kt new file mode 100644 index 00000000..0685efbd --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptions.androidxCommon.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.foundation.text + +import com.huanshankeji.compose.foundation.text.input.toPlatformValue +import androidx.compose.foundation.text.KeyboardOptions as PlatformKeyboardOptions + +fun KeyboardOptions.toPlatformValue(): PlatformKeyboardOptions = + PlatformKeyboardOptions( + capitalization.toPlatformValue(), + autoCorrect ?: true, + keyboardType.toPlatformValue(), + imeAction.toPlatformValue(), + platformImeOptions?.platformValue + ) diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/ImeAction.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/ImeAction.androidxCommon.kt new file mode 100644 index 00000000..1299ff15 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/ImeAction.androidxCommon.kt @@ -0,0 +1,20 @@ +package com.huanshankeji.compose.foundation.text.input + +import androidx.compose.ui.text.input.ImeAction as PlatformImeAction + +fun ImeAction.toPlatformValue(): PlatformImeAction = + when (this) { + ImeAction.Default -> PlatformImeAction.Default + ImeAction.None -> PlatformImeAction.None + ImeAction.Go -> PlatformImeAction.Go + ImeAction.Search -> PlatformImeAction.Search + ImeAction.Send -> PlatformImeAction.Search + ImeAction.Previous -> PlatformImeAction.Previous + ImeAction.Next -> PlatformImeAction.Next + ImeAction.Done -> PlatformImeAction.Done + + else -> throw IllegalArgumentException(toString()) + } + +fun ImeAction?.toPlatformValue(): PlatformImeAction = + this?.toPlatformValue() ?: PlatformImeAction.Default diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardCapitalization.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardCapitalization.androidxCommon.kt new file mode 100644 index 00000000..f448c868 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardCapitalization.androidxCommon.kt @@ -0,0 +1,16 @@ +package com.huanshankeji.compose.foundation.text.input + +import androidx.compose.ui.text.input.KeyboardCapitalization as PlatformKeyboardCapitalization + +fun KeyboardCapitalization.toPlatformValue(): PlatformKeyboardCapitalization = + when (this) { + KeyboardCapitalization.None -> PlatformKeyboardCapitalization.None + KeyboardCapitalization.Characters -> PlatformKeyboardCapitalization.Characters + KeyboardCapitalization.Words -> PlatformKeyboardCapitalization.Words + KeyboardCapitalization.Sentences -> PlatformKeyboardCapitalization.Sentences + + else -> throw IllegalArgumentException(toString()) + } + +fun KeyboardCapitalization?.toPlatformValue(): PlatformKeyboardCapitalization = + this?.toPlatformValue() ?: PlatformKeyboardCapitalization.None diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardType.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardType.androidxCommon.kt new file mode 100644 index 00000000..4850f3ad --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardType.androidxCommon.kt @@ -0,0 +1,21 @@ +package com.huanshankeji.compose.foundation.text.input + +import androidx.compose.ui.text.input.KeyboardType as PlatformKeyboardType + +fun KeyboardType.toPlatformValue(): PlatformKeyboardType = + when (this) { + KeyboardType.Text -> PlatformKeyboardType.Text + KeyboardType.Ascii -> PlatformKeyboardType.Ascii + KeyboardType.Number -> PlatformKeyboardType.Number + KeyboardType.Phone -> PlatformKeyboardType.Phone + KeyboardType.Uri -> PlatformKeyboardType.Uri + KeyboardType.Email -> PlatformKeyboardType.Email + KeyboardType.Password -> PlatformKeyboardType.Password + KeyboardType.NumberPassword -> PlatformKeyboardType.NumberPassword + KeyboardType.Decimal -> PlatformKeyboardType.Decimal + + else -> throw IllegalArgumentException(toString()) + } + +fun KeyboardType?.toPlatformValue(): PlatformKeyboardType = + this?.toPlatformValue() ?: PlatformKeyboardType.Text diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/PlatformImeOptions.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/PlatformImeOptions.androidxCommon.kt new file mode 100644 index 00000000..49429ee3 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/foundation/text/input/PlatformImeOptions.androidxCommon.kt @@ -0,0 +1,3 @@ +package com.huanshankeji.compose.foundation.text.input + +actual class PlatformImeOptions(val platformValue: androidx.compose.ui.text.input.PlatformImeOptions) \ 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/layout/Intrinsic.androidxCommon.kt new file mode 100644 index 00000000..836383f0 --- /dev/null +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.androidxCommon.kt @@ -0,0 +1,21 @@ +package com.huanshankeji.compose.layout + +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier +import androidx.compose.foundation.layout.IntrinsicSize as PlatformIntrinsicSize + +@Stable +actual fun Modifier.width(intrinsicSize: IntrinsicSize): Modifier = + platformModify { width(intrinsicSize.toPlatformValue()) } + +@Stable +actual fun Modifier.height(intrinsicSize: IntrinsicSize): Modifier = + platformModify { height(intrinsicSize.toPlatformValue()) } + +fun IntrinsicSize.toPlatformValue() = + when (this) { + IntrinsicSize.Min -> PlatformIntrinsicSize.Min + IntrinsicSize.Max -> PlatformIntrinsicSize.Max + } 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/layout/Size.androidxCommon.kt index 2331ed46..58298a04 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/layout/Size.androidxCommon.kt @@ -44,5 +44,18 @@ actual fun Modifier.fillMaxWidth(@FloatRange(from = 0.0, to = 1.0) fraction: Flo actual fun Modifier.fillMaxHeight(@FloatRange(from = 0.0, to = 1.0) fraction: Float): Modifier = platformModify { fillMaxHeight(fraction) } +@Stable actual fun Modifier.fillMaxSize(@FloatRange(from = 0.0, to = 1.0) fraction: Float): Modifier = platformModify { fillMaxSize(fraction) } + +@Stable +actual fun Modifier.wrapContentWidth(): Modifier = + platformModify { wrapContentWidth() } + +@Stable +actual fun Modifier.wrapContentHeight(): Modifier = + platformModify { wrapContentHeight() } + +@Stable +actual fun Modifier.wrapContentSize(): Modifier = + platformModify { wrapContentSize() } diff --git a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/Modifier.androidxCommon.kt b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/Modifier.androidxCommon.kt index 3473e60d..6eed0153 100644 --- a/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/Modifier.androidxCommon.kt +++ b/compose-multiplatform-common/src/androidxCommonMain/kotlin/com/huanshankeji/compose/ui/Modifier.androidxCommon.kt @@ -3,8 +3,9 @@ package com.huanshankeji.compose.ui import kotlin.jvm.JvmInline import androidx.compose.ui.Modifier as PlatformModifier -//actual typealias Modifier = androidx.compose.ui.Modifier // this doesn't work +typealias PlatformModifier = PlatformModifier +//actual typealias Modifier = androidx.compose.ui.Modifier // this doesn't work actual interface Modifier { val platformModifier: PlatformModifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/Content.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/Content.kt new file mode 100644 index 00000000..a289f126 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/Content.kt @@ -0,0 +1,7 @@ +package com.huanshankeji.compose + +/* +// This API seems to reduce the readability of code +fun (@Composable () -> Unit).toContentWithoutScope(): @Composable Scope.() -> Unit = + { this@toContentWithoutScope() } +*/ diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ExtRecommendedApi.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ExtRecommendedApi.kt new file mode 100644 index 00000000..8c3ae1cb --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/ExtRecommendedApi.kt @@ -0,0 +1,9 @@ +package com.huanshankeji.compose + +@RequiresOptIn( + "This API is more akin to the original `androidx.compose` one but does not follow the conventions and cannot produce the perfect outcome on JS (Compose HTML). " + + "Consider using the corresponding alternative in the `ext` package instead if you have time to refactor the code.", + RequiresOptIn.Level.WARNING +) +@Retention(AnnotationRetention.BINARY) +annotation class ExtRecommendedApi \ No newline at end of file diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/ExperimentalFoundationApi.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/ExperimentalFoundationApi.kt new file mode 100644 index 00000000..c9e1b68d --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/ExperimentalFoundationApi.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.foundation + +@RequiresOptIn( + "This foundation API is experimental and is likely to change or be removed in the " + + "future. See `androidx.compose.foundation.ExperimentalFoundationApi`." +) +@Retention(AnnotationRetention.BINARY) +annotation class ExperimentalFoundationApi 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 new file mode 100644 index 00000000..e9415572 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/OnClick.kt @@ -0,0 +1,5 @@ +package com.huanshankeji.compose.foundation + +import com.huanshankeji.compose.ui.Modifier + +expect fun Modifier.onClick(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 new file mode 100644 index 00000000..e5ca0111 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/Scroll.kt @@ -0,0 +1,29 @@ +package com.huanshankeji.compose.foundation + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun rememberScrollState(initial: Int = 0): ScrollState + +/** + * Not working on JS yet and delegating to [Unit]. + */ +@Stable +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. + */ +expect fun Modifier.verticalScroll( + state: ScrollState + /* + enabled: Boolean = true, + flingBehavior: FlingBehavior? = null, + reverseScrolling: Boolean = false + */ +): Modifier + +expect fun Modifier.horizontalScroll(state: ScrollState): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.kt index 5017ea6f..0691a84e 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Column.kt @@ -1,9 +1,11 @@ package com.huanshankeji.compose.foundation.layout +import androidx.annotation.FloatRange import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import com.huanshankeji.compose.ui.Alignment import com.huanshankeji.compose.ui.Modifier +import kotlin.jvm.JvmInline @Composable expect fun Column( @@ -13,8 +15,25 @@ expect fun Column( content: @Composable ColumnScope.() -> Unit ) + +expect interface PlatformColumnScope + //@LayoutScopeMarker expect interface ColumnScope { + val platformValue: PlatformColumnScope + + @JvmInline + value class Impl(override val platformValue: PlatformColumnScope) : ColumnScope + + @Stable + open fun Modifier.weight( + @FloatRange(from = 0.0, fromInclusive = false) + weight: Float + ): Modifier + @Stable open fun Modifier.align(alignment: Alignment.Horizontal): Modifier } + +fun (@Composable (ColumnScope.() -> Unit)).toCommonColumnScopeContent(): @Composable PlatformColumnScope.() -> Unit = + { ColumnScope.Impl(this).this@toCommonColumnScopeContent() } diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.kt index 4accffe4..f61df2ca 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Row.kt @@ -1,5 +1,6 @@ package com.huanshankeji.compose.foundation.layout +import androidx.annotation.FloatRange import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import com.huanshankeji.compose.ui.Alignment @@ -13,8 +14,24 @@ expect fun Row( content: @Composable RowScope.() -> Unit ) + +expect interface PlatformRowScope + //@LayoutScopeMarker expect interface RowScope { + val platformValue: PlatformRowScope + + value class Impl(override val platformValue: PlatformRowScope) : RowScope + + @Stable + open fun Modifier.weight( + @FloatRange(from = 0.0, fromInclusive = false) + weight: Float + ): Modifier + @Stable open fun Modifier.align(alignment: Alignment.Vertical): Modifier } + +fun (@Composable (RowScope.() -> Unit)).toPlatformRowScopeContent(): @Composable PlatformRowScope.() -> Unit = + { RowScope.Impl(this).this@toPlatformRowScopeContent() } diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Spacer.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Spacer.kt new file mode 100644 index 00000000..3e010922 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/layout/Spacer.kt @@ -0,0 +1,7 @@ +package com.huanshankeji.compose.foundation.layout + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun Spacer(modifier: 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 new file mode 100644 index 00000000..fddc0ad0 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.kt @@ -0,0 +1,123 @@ +package com.huanshankeji.compose.foundation.lazy + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.Arrangement +import com.huanshankeji.compose.ui.Alignment +import com.huanshankeji.compose.ui.Modifier + +// copied and adapted from `LazyDsl.kt` in `androidx.compose.foundation.lazy` + + +/* +@LazyScopeMarker +@JvmDefaultWithCompatibility +*/ +expect class LazyListScope { + fun item( + key: Any? = null, + contentType: Any? = null, + content: @Composable LazyItemScope.() -> Unit + ) + + fun items( + count: Int, + key: ((index: Int) -> Any)? = null, + contentType: (index: Int) -> Any? = { null }, + itemContent: @Composable LazyItemScope.(index: Int) -> Unit + ) + + /* + @ExperimentalFoundationApi + fun stickyHeader( + key: Any? = null, + contentType: Any? = null, + content: @Composable LazyItemScope.() -> Unit + ) + */ +} + +inline fun LazyListScope.items( + items: List, + noinline key: ((item: T) -> Any)? = null, + noinline contentType: (item: T) -> Any? = { null }, + crossinline itemContent: @Composable LazyItemScope.(item: T) -> Unit +) = items( + count = items.size, + key = if (key != null) { index: Int -> key(items[index]) } else null, + contentType = { index: Int -> contentType(items[index]) } +) { + itemContent(items[it]) +} + +inline fun LazyListScope.itemsIndexed( + items: List, + noinline key: ((index: Int, item: T) -> Any)? = null, + crossinline contentType: (index: Int, item: T) -> Any? = { _, _ -> null }, + crossinline itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit +) = items( + count = items.size, + key = if (key != null) { index: Int -> key(index, items[index]) } else null, + contentType = { index -> contentType(index, items[index]) } +) { + itemContent(it, items[it]) +} + +inline fun LazyListScope.items( + items: Array, + noinline key: ((item: T) -> Any)? = null, + noinline contentType: (item: T) -> Any? = { null }, + crossinline itemContent: @Composable LazyItemScope.(item: T) -> Unit +) = items( + count = items.size, + key = if (key != null) { index: Int -> key(items[index]) } else null, + contentType = { index: Int -> contentType(items[index]) } +) { + itemContent(items[it]) +} + +inline fun LazyListScope.itemsIndexed( + items: Array, + noinline key: ((index: Int, item: T) -> Any)? = null, + crossinline contentType: (index: Int, item: T) -> Any? = { _, _ -> null }, + crossinline itemContent: @Composable LazyItemScope.(index: Int, item: T) -> Unit +) = items( + count = items.size, + key = if (key != null) { index: Int -> key(index, items[index]) } else null, + contentType = { index -> contentType(index, items[index]) } +) { + itemContent(it, items[it]) +} + +/** + * No need to be lazy on JS. + */ +@Composable +expect fun LazyRow( + modifier: Modifier = Modifier, + //state: LazyListState = rememberLazyListState(), + //contentPadding: PaddingValues = PaddingValues(0.dp), + reverseLayout: Boolean = false, + horizontalArrangement: Arrangement.Horizontal = + if (!reverseLayout) Arrangement.Start else Arrangement.End, + verticalAlignment: Alignment.Vertical = Alignment.Top, + //flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), + //userScrollEnabled: Boolean = true, // This property works together with `state`. + content: LazyListScope.() -> Unit +) + +/** + * No need to be lazy on JS. + */ +@Composable +expect fun LazyColumn( + modifier: Modifier = Modifier, + //state: LazyListState = rememberLazyListState(), + //contentPadding: PaddingValues = PaddingValues(0.dp), + reverseLayout: Boolean = false, + verticalArrangement: Arrangement.Vertical = + if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, + horizontalAlignment: Alignment.Horizontal = Alignment.Start, + //flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(), + //userScrollEnabled: Boolean = true, // This property works together with `state`. + content: LazyListScope.() -> Unit +) diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.kt new file mode 100644 index 00000000..cb641a4a --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.kt @@ -0,0 +1,37 @@ +package com.huanshankeji.compose.foundation.lazy + +import androidx.annotation.FloatRange +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier + +@Stable +/* +@LazyScopeMarker +@JvmDefaultWithCompatibility +*/ +expect class LazyItemScope { + fun Modifier.fillParentMaxSize( + @FloatRange(from = 0.0, to = 1.0) + fraction: Float = 1f + ): Modifier + + fun Modifier.fillParentMaxWidth( + @FloatRange(from = 0.0, to = 1.0) + fraction: Float = 1f + ): Modifier + + fun Modifier.fillParentMaxHeight( + @FloatRange(from = 0.0, to = 1.0) + fraction: Float = 1f + ): Modifier + + /* + @ExperimentalFoundationApi + fun Modifier.animateItemPlacement( + animationSpec: FiniteAnimationSpec = spring( + stiffness = Spring.StiffnessMediumLow, + visibilityThreshold = androidx.compose.ui.unit.IntOffset.VisibilityThreshold + ) + ): Modifier + */ +} diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardActions.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardActions.kt new file mode 100644 index 00000000..d7703ea5 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardActions.kt @@ -0,0 +1,27 @@ +package com.huanshankeji.compose.foundation.text + +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.foundation.text.input.ImeAction +import kotlin.jvm.JvmInline + +// copied and adapted from `androidx.compose.foundation.text` + +@JvmInline +value class KeyboardActions( + val onAny: (KeyboardActionScope.() -> Unit)? = null, +) { + companion object { + @Stable + val Default: KeyboardActions = KeyboardActions() + } +} + +interface KeyboardActionScope { + fun defaultKeyboardAction(imeAction: ImeAction) + + object DoNothingInstance : KeyboardActionScope { + override fun defaultKeyboardAction(imeAction: ImeAction) { + // do nothing for now + } + } +} diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptions.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptions.kt new file mode 100644 index 00000000..7fa37f46 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptions.kt @@ -0,0 +1,25 @@ +package com.huanshankeji.compose.foundation.text + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.Stable +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.foundation.text.input.PlatformImeOptions + +// copied and adapted from `androidx.compose.foundation.text.KeyboardOptions` +@Immutable +class KeyboardOptions( + val capitalization: KeyboardCapitalization? = null, + val autoCorrect: Boolean? = null, + val keyboardType: KeyboardType? = null, + val imeAction: ImeAction? = null, + val platformImeOptions: PlatformImeOptions? = null +) { + companion object { + @Stable + val Default = KeyboardOptions() + } + + // `copy`, `equals`, `hashCode`, and `toString`, are removed. I am hoping to generated them with a plugin to reduce duplicate code. +} diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/ImeAction.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/ImeAction.kt new file mode 100644 index 00000000..88b31c2d --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/ImeAction.kt @@ -0,0 +1,48 @@ +package com.huanshankeji.compose.foundation.text.input + +import androidx.compose.runtime.Stable + +// copied and adapted from `androidx.compose.foundation.text.input.ImeAction` +@kotlin.jvm.JvmInline +value class ImeAction internal constructor(@Suppress("unused") private val value: Int) { + + override fun toString(): String { + return when (this) { + None -> "None" + Default -> "Default" + Go -> "Go" + Search -> "Search" + Send -> "Send" + Previous -> "Previous" + Next -> "Next" + Done -> "Done" + else -> "Invalid" + } + } + + companion object { + @Stable + val Default: ImeAction = ImeAction(1) + + @Stable + val None: ImeAction = ImeAction(0) + + @Stable + val Go: ImeAction = ImeAction(2) + + @Stable + val Search: ImeAction = ImeAction(3) + + @Stable + val Send: ImeAction = ImeAction(4) + + @Stable + val Previous: ImeAction = ImeAction(5) + + @Stable + val Next: ImeAction = ImeAction(6) + + @Stable + val Done: ImeAction = ImeAction(7) + } +} diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardCapitalization.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardCapitalization.kt new file mode 100644 index 00000000..913b602e --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardCapitalization.kt @@ -0,0 +1,31 @@ +package com.huanshankeji.compose.foundation.text.input + +import androidx.compose.runtime.Stable + +// copied and adapted from `androidx.compose.foundation.text.input.KeyboardCapitalization` +@kotlin.jvm.JvmInline +value class KeyboardCapitalization internal constructor(internal val value: Int) { + override fun toString(): String { + return when (this) { + None -> "None" + Characters -> "Characters" + Words -> "Words" + Sentences -> "Sentences" + else -> "Invalid" + } + } + + companion object { + @Stable + val None = KeyboardCapitalization(0) + + @Stable + val Characters = KeyboardCapitalization(1) + + @Stable + val Words = KeyboardCapitalization(2) + + @Stable + val Sentences = KeyboardCapitalization(3) + } +} diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardType.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardType.kt new file mode 100644 index 00000000..cf20c278 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardType.kt @@ -0,0 +1,55 @@ +package com.huanshankeji.compose.foundation.text.input + +import androidx.compose.runtime.Stable + +// copied and adapted from `androidx.compose.foundation.text.input.KeyboardType` +@kotlin.jvm.JvmInline +value class KeyboardType internal constructor(@Suppress("unused") private val value: Int) { + + override fun toString(): String { + return when (this) { + Text -> "Text" + Ascii -> "Ascii" + Number -> "Number" + Phone -> "Phone" + Uri -> "Uri" + Email -> "Email" + Password -> "Password" + NumberPassword -> "NumberPassword" + Decimal -> "Decimal" + else -> "Invalid" + } + } + + companion object { + @Stable + val Text: KeyboardType = KeyboardType(1) + + // doesn't work on JS yet + @Stable + val Ascii: KeyboardType = KeyboardType(2) + + @Stable + val Number: KeyboardType = KeyboardType(3) + + @Stable + val Phone: KeyboardType = KeyboardType(4) + + @Stable + val Uri: KeyboardType = KeyboardType(5) + + @Stable + val Email: KeyboardType = KeyboardType(6) + + // doesn't work on JS yet + @Stable + val Password: KeyboardType = KeyboardType(7) + + // doesn't work on JS yet + @Stable + val NumberPassword: KeyboardType = KeyboardType(8) + + @Stable + val Decimal: KeyboardType = KeyboardType(9) + } +} diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/PlatformImeOptions.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/PlatformImeOptions.kt new file mode 100644 index 00000000..960c21c3 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/foundation/text/input/PlatformImeOptions.kt @@ -0,0 +1,3 @@ +package com.huanshankeji.compose.foundation.text.input + +expect class PlatformImeOptions \ No newline at end of file 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/layout/Intrinsic.kt new file mode 100644 index 00000000..52ce3016 --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.compose.layout + +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier + +@Stable +expect fun Modifier.width(intrinsicSize: IntrinsicSize): Modifier + +@Stable +expect fun Modifier.height(intrinsicSize: IntrinsicSize): Modifier + +enum class IntrinsicSize { Min, Max } 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/layout/Size.kt index 25b28433..d6c73270 100644 --- a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Size.kt +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/layout/Size.kt @@ -42,4 +42,14 @@ expect fun Modifier.fillMaxWidth(@FloatRange(from = 0.0, to = 1.0) fraction: Flo @Stable expect fun Modifier.fillMaxHeight(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f): Modifier +@Stable expect fun Modifier.fillMaxSize(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f): Modifier + +@Stable +expect fun Modifier.wrapContentWidth(): Modifier + +@Stable +expect fun Modifier.wrapContentHeight(): Modifier + +@Stable +expect fun Modifier.wrapContentSize(): Modifier diff --git a/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/runtime/DeferredComposableRunner.kt b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/runtime/DeferredComposableRunner.kt new file mode 100644 index 00000000..f82579ce --- /dev/null +++ b/compose-multiplatform-common/src/commonMain/kotlin/com/huanshankeji/compose/runtime/DeferredComposableRunner.kt @@ -0,0 +1,20 @@ +package com.huanshankeji.compose.runtime + +import androidx.compose.runtime.Composable + +class DeferredComposableRunner { + private var composables: MutableList<@Composable () -> Unit>? = null + fun addComposable(composable: @Composable () -> Unit) { + composables!!.add(composable) + } + + /** Add the composable functions with the non-composable functions and then invoke them. */ + @Composable + fun ComposableRun(content: () -> Unit) { + composables = mutableListOf() + content() + for (composable in composables!!) + composable() + composables = null + } +} diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ContentDescription.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ContentDescription.kt new file mode 100644 index 00000000..3782e2fb --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ContentDescription.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.compose + +import org.jetbrains.compose.web.attributes.AttrsScope + +fun AttrsScope<*>.contentDescription(contentDescription: String?) = + contentDescription?.let { + // aria-label's semantics don't seem to meet the requirements perfectly because it labels an interactive element + // see: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label, https://stackoverflow.com/questions/22039910/what-is-aria-label-and-how-should-i-use-it + //attr("aria-label", it) + + title(it) + } 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 new file mode 100644 index 00000000..2856d5fb --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/OnClick.js.kt @@ -0,0 +1,7 @@ +package com.huanshankeji.compose.foundation + +import com.huanshankeji.compose.ui.Modifier +import com.varabyte.kobweb.compose.ui.modifiers.onClick + +actual fun Modifier.onClick(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 new file mode 100644 index 00000000..4e8a12b7 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/Scroll.js.kt @@ -0,0 +1,32 @@ +package com.huanshankeji.compose.foundation + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.ext.css.horizontalScroll +import com.huanshankeji.compose.foundation.ext.css.verticalScroll +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.PlatformModifier +import com.varabyte.kobweb.compose.ui.styleModifier + +fun PlatformModifier.verticalScroll() = + styleModifier { verticalScroll() } + +fun PlatformModifier.horizontalScroll() = + styleModifier { horizontalScroll() } + +val verticalScrollPlatformModifier = PlatformModifier.verticalScroll() +val horizontalScrollPlatformModifier = PlatformModifier.horizontalScroll() + + +@Composable +actual fun rememberScrollState(initial: Int): ScrollState = + ScrollState + +actual typealias ScrollState = Unit + +actual fun Modifier.verticalScroll(state: ScrollState): Modifier = + platformModify { verticalScroll() } + + +actual fun Modifier.horizontalScroll(state: ScrollState): Modifier = + platformModify { horizontalScroll() } + diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/ext/css/Scroll.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/ext/css/Scroll.kt new file mode 100644 index 00000000..9eaca85c --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/ext/css/Scroll.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.foundation.ext.css + +import com.varabyte.kobweb.compose.css.Overflow +import com.varabyte.kobweb.compose.css.overflowX +import com.varabyte.kobweb.compose.css.overflowY +import org.jetbrains.compose.web.css.StyleScope + +fun StyleScope.horizontalScroll() = + overflowX(Overflow.Auto) + +fun StyleScope.verticalScroll() = + //overflowY(Overflow.Scroll) + overflowY(Overflow.Auto) 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 8d23c415..57dc24cf 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 @@ -5,22 +5,24 @@ import androidx.compose.runtime.Immutable 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.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 -import com.varabyte.kobweb.compose.ui.Modifier as PlatformModifier @Composable actual fun Box( modifier: Modifier, contentAlignment: Alignment, content: @Composable BoxScope.() -> Unit -) = +) { + AddKobwebComposeStyleSheet() PlatformBox( PlatformModifier.sizeFitContent().then(modifier.platformModifier), contentAlignment.platformValue, ) { BoxScope.Impl(this).content() } +} @Composable actual fun Box(modifier: Modifier) = 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 4a0d132f..be3b9ea2 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 @@ -1,17 +1,13 @@ package com.huanshankeji.compose.foundation.layout +import androidx.annotation.FloatRange import androidx.compose.runtime.Composable 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.varabyte.kobweb.compose.foundation.layout.LayoutScopeMarker -import com.varabyte.kobweb.compose.ui.modifiers.display -import com.varabyte.kobweb.compose.ui.modifiers.flexDirection -import org.jetbrains.compose.web.css.DisplayStyle -import org.jetbrains.compose.web.css.FlexDirection -import com.varabyte.kobweb.compose.foundation.layout.ColumnScope as PlatformColumnScope -import com.varabyte.kobweb.compose.ui.Modifier as PlatformModifier @Composable actual fun Column( @@ -19,20 +15,33 @@ actual fun Column( verticalArrangement: Arrangement.Vertical, horizontalAlignment: Alignment.Horizontal, content: @Composable ColumnScope.() -> Unit -) = +) { + AddKobwebComposeStyleSheet() com.varabyte.kobweb.compose.foundation.layout.Column( - PlatformModifier.display(DisplayStyle.Flex).flexDirection(FlexDirection.Column) + PlatformModifier .sizeFitContent() // "fit-content" is added to make it consistent with the `androidx` one .then(modifier.platformModifier), verticalArrangement.platformValue, - horizontalAlignment.platformValue - ) { ColumnScope.Impl(this).content() } + horizontalAlignment.platformValue, + content = content.toCommonColumnScopeContent() + ) +} + + +actual typealias PlatformColumnScope = com.varabyte.kobweb.compose.foundation.layout.ColumnScope @LayoutScopeMarker actual interface ColumnScope { - val platformValue: PlatformColumnScope + actual val platformValue: PlatformColumnScope - value class Impl(override val platformValue: PlatformColumnScope) : ColumnScope + actual value class Impl(override val platformValue: PlatformColumnScope) : ColumnScope + + @Stable + actual fun Modifier.weight( + @FloatRange(from = 0.0, fromInclusive = false) + weight: Float + ): Modifier = + with(platformValue) { platformModify { 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/KobwebCompose.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/KobwebCompose.kt new file mode 100644 index 00000000..d0796c6d --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/KobwebCompose.kt @@ -0,0 +1,9 @@ +package com.huanshankeji.compose.foundation.layout + +import androidx.compose.runtime.Composable +import com.varabyte.kobweb.compose.style.KobwebComposeStyleSheet +import org.jetbrains.compose.web.css.Style + +@Composable +internal fun AddKobwebComposeStyleSheet() = + Style(KobwebComposeStyleSheet) 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 e628cfa7..04b1e3a5 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 @@ -1,18 +1,13 @@ package com.huanshankeji.compose.foundation.layout +import androidx.annotation.FloatRange import androidx.compose.runtime.Composable 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.varabyte.kobweb.compose.foundation.layout.LayoutScopeMarker -import com.varabyte.kobweb.compose.ui.modifiers.display -import com.varabyte.kobweb.compose.ui.modifiers.flexDirection -import org.jetbrains.compose.web.css.DisplayStyle -import org.jetbrains.compose.web.css.FlexDirection -import com.varabyte.kobweb.compose.foundation.layout.RowScope as PlatformRowScope - -import com.varabyte.kobweb.compose.ui.Modifier as PlatformModifier @Composable actual fun Row( @@ -20,20 +15,33 @@ actual fun Row( horizontalArrangement: Arrangement.Horizontal, verticalAlignment: Alignment.Vertical, content: @Composable RowScope.() -> Unit -) = +) { + AddKobwebComposeStyleSheet() com.varabyte.kobweb.compose.foundation.layout.Row( - PlatformModifier.display(DisplayStyle.Flex).flexDirection(FlexDirection.Row) + PlatformModifier .sizeFitContent() .then(modifier.platformModifier), horizontalArrangement.platformValue, - verticalAlignment.platformValue - ) { RowScope.Impl(this).content() } + verticalAlignment.platformValue, + content = content.toPlatformRowScopeContent() + ) +} + + +actual typealias PlatformRowScope = com.varabyte.kobweb.compose.foundation.layout.RowScope @LayoutScopeMarker actual interface RowScope { - val platformValue: PlatformRowScope + actual val platformValue: PlatformRowScope + + actual value class Impl(override val platformValue: PlatformRowScope) : RowScope - value class Impl(override val platformValue: PlatformRowScope) : RowScope + @Stable + actual fun Modifier.weight( + @FloatRange(from = 0.0, fromInclusive = false) + weight: Float + ): Modifier = + with(platformValue) { platformModify { weight(weight) } } @Stable actual fun Modifier.align(alignment: Alignment.Vertical): Modifier = diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Spacer.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Spacer.js.kt new file mode 100644 index 00000000..2d2377cb --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/layout/Spacer.js.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.foundation.layout + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs +import org.jetbrains.compose.web.dom.Div + +@Composable +actual fun Spacer(modifier: Modifier) { + AddKobwebComposeStyleSheet() + Div(attrs = modifier.toAttrs { classes("kobweb-spacer") }) + //com.varabyte.kobweb.compose.foundation.layout.Spacer() // use this when `Modifier` is supported +} 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 new file mode 100644 index 00000000..73a7e70b --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyDsl.js.kt @@ -0,0 +1,78 @@ +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.runtime.DeferredComposableRunner +import com.huanshankeji.compose.ui.Alignment +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toCommonModifier + +/* +@LazyScopeMarker +@JvmDefaultWithCompatibility +*/ +actual class LazyListScope { + private val deferredComposableRunner = DeferredComposableRunner() + + private fun addComposable(composable: @Composable () -> Unit) = + deferredComposableRunner.addComposable(composable) + + @Composable + internal fun ComposableRun(content: LazyListScope.() -> Unit) = + deferredComposableRunner.ComposableRun { content() } + + actual fun item( + key: Any?, + contentType: Any?, + content: @Composable LazyItemScope.() -> Unit + ) = + addComposable { LazyItemScope.content() } + + actual fun items( + count: Int, + key: ((index: Int) -> Any)?, + contentType: (index: Int) -> Any?, + itemContent: @Composable LazyItemScope.(index: Int) -> Unit + ) = + addComposable { + repeat(count) { index -> + LazyItemScope.itemContent(index) + } + } +} + +@Composable +actual fun LazyRow( + modifier: Modifier, + 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(), + horizontalArrangement, + verticalAlignment + ) { + LazyListScope().ComposableRun(content) + } + +@Composable +actual fun LazyColumn( + modifier: Modifier, + 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(), + verticalArrangement, + horizontalAlignment + ) { + LazyListScope().ComposableRun(content) + } 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 new file mode 100644 index 00000000..945d09ee --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/lazy/LazyItemScope.js.kt @@ -0,0 +1,35 @@ +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.ui.Modifier + +@Stable +/* +@LazyScopeMarker +@JvmDefaultWithCompatibility +*/ +actual object LazyItemScope { + // I am not sure whether these implementations work perfectly. + + actual fun Modifier.fillParentMaxSize( + @FloatRange(from = 0.0, to = 1.0) + fraction: Float + ): Modifier = + fillMaxSize(fraction) + + actual fun Modifier.fillParentMaxWidth( + @FloatRange(from = 0.0, to = 1.0) + fraction: Float + ): Modifier = + fillMaxWidth(fraction) + + actual fun Modifier.fillParentMaxHeight( + @FloatRange(from = 0.0, to = 1.0) + fraction: Float + ): Modifier = + fillMaxHeight(fraction) +} \ No newline at end of file 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 dc0fe0ce..b038591c 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,7 @@ package com.huanshankeji.compose.foundation.text import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier -import com.varabyte.kobweb.compose.ui.toAttrs +import com.huanshankeji.compose.ui.toAttrs import org.jetbrains.compose.web.dom.Span import org.jetbrains.compose.web.dom.Text @@ -17,4 +17,4 @@ actual fun BasicText(text: String) = @Composable actual fun BasicText(text: String, modifier: Modifier) = - Span(modifier.platformModifier.toAttrs()) { Text(text) } + Span(modifier.toAttrs()) { Text(text) } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptions.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptions.js.kt new file mode 100644 index 00000000..517ded83 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptions.js.kt @@ -0,0 +1,27 @@ +package com.huanshankeji.compose.foundation.text + +import com.huanshankeji.compose.foundation.text.input.toAutoCapitalizeAttrValue +import com.huanshankeji.compose.foundation.text.input.toInputMode +import com.huanshankeji.compose.web.attributes.autocorrect +import com.huanshankeji.compose.web.attributes.ext.autoCapitalizeRequiringValid +import com.huanshankeji.compose.web.attributes.ext.enterKeyHintIfValid +import org.jetbrains.compose.web.attributes.AttrsScope + +fun AttrsScope<*>.attrsFrom(keyboardOptions: KeyboardOptions) { + with(keyboardOptions) { + capitalization?.let { autoCapitalizeRequiringValid(it.toAutoCapitalizeAttrValue()) } + autoCorrect?.let { + // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck + spellCheck(it) + autocorrect(it) + } + // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode + keyboardType?.toInputMode()?.let { inputMode(it) } + imeAction?.let { + // Also see: https://stackoverflow.com/questions/24278088/showing-search-button-as-submit-button-in-mobile-devices-keyboards + @Suppress("DEPRECATION") + enterKeyHintIfValid(it.toString().decapitalize()) + } + //platformImeOptions + } +} diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptionsAndKeyboardActions.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptionsAndKeyboardActions.js.kt new file mode 100644 index 00000000..cbddafa1 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/KeyboardOptionsAndKeyboardActions.js.kt @@ -0,0 +1,14 @@ +package com.huanshankeji.compose.foundation.text + +import org.jetbrains.compose.web.attributes.AttrsScope +import org.w3c.dom.HTMLElement + +fun AttrsScope.attrsFrom(keyboardOptions: KeyboardOptions, keyboardActions: KeyboardActions) { + attrsFrom(keyboardOptions) + keyboardActions.onAny?.let { + onKeyDown { + if (it.key == "Enter") + KeyboardActionScope.DoNothingInstance.it() + } + } +} diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardCapitalization.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardCapitalization.js.kt new file mode 100644 index 00000000..c6e93303 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardCapitalization.js.kt @@ -0,0 +1,5 @@ +package com.huanshankeji.compose.foundation.text.input + +fun KeyboardCapitalization.toAutoCapitalizeAttrValue() = + @Suppress("DEPRECATION") + toString().decapitalize() diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardType.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardType.js.kt new file mode 100644 index 00000000..6d0d436c --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/input/KeyboardType.js.kt @@ -0,0 +1,19 @@ +package com.huanshankeji.compose.foundation.text.input + +import org.jetbrains.compose.web.attributes.InputMode + +fun KeyboardType.toInputMode(): InputMode? = + when (this) { + KeyboardType.Text -> InputMode.Text + KeyboardType.Ascii -> null + KeyboardType.Number -> InputMode.Numeric + KeyboardType.Phone -> InputMode.Tel + KeyboardType.Uri -> InputMode.Url + KeyboardType.Email -> InputMode.Email + // TODO + KeyboardType.Password -> null + KeyboardType.NumberPassword -> null + KeyboardType.Decimal -> InputMode.Decimal + + else -> throw IllegalArgumentException(toString()) + } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/input/PlatformImeOptions.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/input/PlatformImeOptions.js.kt new file mode 100644 index 00000000..dea83f39 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/foundation/text/input/PlatformImeOptions.js.kt @@ -0,0 +1,3 @@ +package com.huanshankeji.compose.foundation.text.input + +actual class PlatformImeOptions \ No newline at end of file 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/layout/Intrinsic.js.kt new file mode 100644 index 00000000..573b73c3 --- /dev/null +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/layout/Intrinsic.js.kt @@ -0,0 +1,30 @@ +package com.huanshankeji.compose.layout + +import androidx.compose.runtime.Stable +import com.huanshankeji.compose.ui.Modifier +import com.varabyte.kobweb.compose.css.Height +import com.varabyte.kobweb.compose.css.Width +import com.varabyte.kobweb.compose.ui.modifiers.height +import com.varabyte.kobweb.compose.ui.modifiers.width + +@Stable +actual fun Modifier.width(intrinsicSize: IntrinsicSize): Modifier = + platformModify { + width( + when (intrinsicSize) { + IntrinsicSize.Min -> Width.MinContent + IntrinsicSize.Max -> Width.MaxContent + } + ) + } + +@Stable +actual fun Modifier.height(intrinsicSize: IntrinsicSize): Modifier = + platformModify { + height( + when (intrinsicSize) { + IntrinsicSize.Min -> Height.MinContent + IntrinsicSize.Max -> Height.MaxContent + } + ) + } 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/layout/Size.js.kt index c3fb36de..124ec2db 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/layout/Size.js.kt @@ -6,6 +6,10 @@ 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 import com.varabyte.kobweb.compose.ui.modifiers.* @Stable @@ -49,5 +53,18 @@ actual fun Modifier.fillMaxWidth(@FloatRange(from = 0.0, to = 1.0) fraction: Flo actual fun Modifier.fillMaxHeight(@FloatRange(from = 0.0, to = 1.0) fraction: Float): Modifier = platformModify { fillMaxHeight(fraction.toPercent()) } +@Stable actual fun Modifier.fillMaxSize(@FloatRange(from = 0.0, to = 1.0) fraction: Float): Modifier = platformModify { fillMaxSize(fraction.toPercent()) } + +@Stable +actual fun Modifier.wrapContentWidth(): Modifier = + platformModify { width(Width.FitContent) } + +@Stable +actual fun Modifier.wrapContentHeight(): Modifier = + platformModify { height(Height.FitContent) } + +@Stable +actual fun Modifier.wrapContentSize(): Modifier = + platformModify { sizeFitContent() } diff --git a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/Modifier.js.kt b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/Modifier.js.kt index fb980ac4..4f0f073d 100644 --- a/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/Modifier.js.kt +++ b/compose-multiplatform-common/src/jsMain/kotlin/com/huanshankeji/compose/ui/Modifier.js.kt @@ -1,9 +1,13 @@ package com.huanshankeji.compose.ui +import com.varabyte.kobweb.compose.ui.toAttrs +import org.jetbrains.compose.web.attributes.AttrsScope +import org.w3c.dom.Element import com.varabyte.kobweb.compose.ui.Modifier as PlatformModifier -//actual typealias Modifier = com.varabyte.kobweb.compose.ui.Modifier // this doesn't work +typealias PlatformModifier = PlatformModifier +//actual typealias Modifier = com.varabyte.kobweb.compose.ui.Modifier // this doesn't work actual interface Modifier { val platformModifier: PlatformModifier @@ -29,4 +33,10 @@ actual interface Modifier { actual companion object : Modifier { override val platformModifier = PlatformModifier } -} \ No newline at end of file +} + +fun PlatformModifier.toCommonModifier() = + Modifier.Impl(this) + +fun > Modifier.toAttrs(finalHandler: (A.() -> Unit)? = null): A.() -> Unit = + platformModifier.toAttrs(finalHandler) 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 fb3a3a13..8a68a73e 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 @@ -32,8 +32,9 @@ actual /*value*/ class Color(val platformValue: PlatformColor) { @Stable actual val Red: Color = Color(PlatformColors.Red) + // The CSS green is #008000 which differs from the Compose one. @Stable - actual val Green: Color = Color(PlatformColors.Green) + actual val Green: Color = Color(PlatformColor.rgb(0x00, 0xFF, 0x00)) @Stable actual val Blue: Color = Color(PlatformColors.Blue) 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 3f9eb0b4..6514c192 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 @@ -2,20 +2,17 @@ package com.huanshankeji.kobweb.compose.ui.modifiers 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.modifiers.height -import com.varabyte.kobweb.compose.ui.modifiers.width +import com.varabyte.kobweb.compose.ui.styleModifier fun Modifier.size(width: Width, height: Height): Modifier = - /* - // not working styleModifier { width(width) height(height) } - */ - width(width).height(height) - +//TODO consider removing and inlining this fun Modifier.sizeFitContent() = size(Width.FitContent, Height.FitContent) diff --git a/compose-multiplatform-material-icons-core/build.gradle.kts b/compose-multiplatform-material-icons-core/build.gradle.kts new file mode 100644 index 00000000..4d75f71c --- /dev/null +++ b/compose-multiplatform-material-icons-core/build.gradle.kts @@ -0,0 +1,36 @@ +import com.huanshankeji.team.`Shreck Ye` +import com.huanshankeji.team.pomForTeamDefaultOpenSource + +plugins { + `lib-conventions` +} + +kotlin { + sourceSets { + /* + Use `implementation`. See: + https://github.com/JetBrains/compose-multiplatform-core/blob/jb-main/compose/material/material-icons-core/build.gradle + https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/compose/material/material-icons-core/build.gradle + */ + commonMain { + dependencies { + implementation(compose.runtime) + } + } + androidxCommonMain { + dependencies { + api("org.jetbrains.compose.material:material-icons-extended:${DependencyVersions.composeMultiplatform}") + } + } + } +} + +publishing.publications.withType { + pomForTeamDefaultOpenSource( + project, + "Compose Multiplatform Material core Icon wrappers", + "Compose Multiplatform Material Design core Icon wrappers for `androidx.compose` and Compose HTML" + ) { + `Shreck Ye`() + } +} diff --git a/compose-multiplatform-material/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 similarity index 100% rename from compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/Icons.androidxCommon.kt rename to compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/Icons.androidxCommon.kt diff --git a/compose-multiplatform-material/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 similarity index 82% rename from compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.androidxCommon.kt rename to compose-multiplatform-material-icons-core/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.androidxCommon.kt index 36e09347..58e5148e 100644 --- a/compose-multiplatform-material/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 @@ -2,6 +2,7 @@ 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 com.huanshankeji.compose.material.icons.Icon import com.huanshankeji.compose.material.icons.Icons @@ -10,3 +11,4 @@ import androidx.compose.material.icons.Icons as PlatformIcons 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 diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/Icons.kt b/compose-multiplatform-material-icons-core/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/Icons.kt similarity index 100% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/Icons.kt rename to compose-multiplatform-material-icons-core/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/Icons.kt diff --git a/compose-multiplatform-material/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 similarity index 87% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.kt rename to compose-multiplatform-material-icons-core/src/commonMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.kt index 52ae6ded..7fa162dc 100644 --- a/compose-multiplatform-material/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 @@ -6,3 +6,4 @@ import com.huanshankeji.compose.material.icons.Icons expect val Icons.Filled.Add: Icon expect val Icons.Filled.Menu: Icon expect val Icons.Filled.Search: Icon +expect val Icons.Filled.Remove: Icon diff --git a/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/Icons.js.kt b/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/Icons.js.kt new file mode 100644 index 00000000..bfca4078 --- /dev/null +++ b/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/Icons.js.kt @@ -0,0 +1,3 @@ +package com.huanshankeji.compose.material.icons + +actual class Icon(val name: String) 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 new file mode 100644 index 00000000..c357931b --- /dev/null +++ b/compose-multiplatform-material-icons-core/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.js.kt @@ -0,0 +1,9 @@ +package com.huanshankeji.compose.material.icons.filled + +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material.icons.Icons + +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") diff --git a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/TextField.androidxCommon.kt b/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/TextField.androidxCommon.kt deleted file mode 100644 index beb65401..00000000 --- a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/TextField.androidxCommon.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.huanshankeji.compose.material - -import androidx.compose.runtime.Composable -import com.huanshankeji.compose.ui.Modifier - -@Composable -actual fun TextField( - value: String, - onValueChange: (String) -> Unit, - modifier: Modifier, - enabled: Boolean, - label: String?, - leadingIcon: @Composable (() -> Unit)?, - trailingIcon: @Composable (() -> Unit)?, -) = - androidx.compose.material.TextField( - value, - onValueChange, - modifier.platformModifier, - enabled = enabled, - label = label?.let { { Text(it) } }, - leadingIcon = leadingIcon, - trailingIcon = trailingIcon - ) diff --git a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/lazy/ext/LazyDsl.androidxCommon.kt b/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/lazy/ext/LazyDsl.androidxCommon.kt deleted file mode 100644 index 6316c00d..00000000 --- a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/lazy/ext/LazyDsl.androidxCommon.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.huanshankeji.compose.material.lazy.ext - -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.runtime.Composable -import com.huanshankeji.compose.ui.Modifier -import androidx.compose.foundation.lazy.LazyItemScope as PlatformLazyItemScope -import androidx.compose.foundation.lazy.LazyListScope as PlatformLazyListScope - -actual class LazyListScope(val platformValue: PlatformLazyListScope) { - actual fun item(key: Any?, contentType: Any?, content: @Composable LazyItemScope.() -> Unit) = - platformValue.item(key, contentType) { LazyItemScope(this).content() } - - actual fun items( - count: Int, - key: ((index: Int) -> Any)?, - contentType: (index: Int) -> Any?, - itemContent: @Composable LazyItemScope.(index: Int) -> Unit - ) = - platformValue.items(count, key, contentType) { LazyItemScope(this).itemContent(it) } - - @OptIn(ExperimentalFoundationApi::class) - actual fun group( - key: Any?, - contentType: Any?, - headerContent: @Composable HeaderScope.() -> Unit, - content: LazyListScope.() -> Unit - ) { - platformValue.stickyHeader(key, contentType) { HeaderScope(this).headerContent() } - content() - } -} - -actual class LazyItemScope(val platformValue: PlatformLazyItemScope) -actual typealias HeaderScope = LazyItemScope - -@Composable -actual fun LazyColumn(modifier: Modifier, content: LazyListScope.() -> Unit) = - // Note that it's actually in the `foundation` package. - androidx.compose.foundation.lazy.LazyColumn(modifier.platformModifier) { - LazyListScope(this).content() - } diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/TextField.kt b/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/TextField.kt deleted file mode 100644 index 2d76f99b..00000000 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/TextField.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.huanshankeji.compose.material - -import androidx.compose.runtime.Composable -import com.huanshankeji.compose.ui.Modifier - -@Composable -expect fun TextField( - value: String, - onValueChange: (String) -> Unit, - modifier: Modifier = Modifier, - enabled: Boolean = true, - label: String? = null, - /* - // These 2 are not the same thing. - placeholder: String? = null, - helperText: String? = null, - */ - leadingIcon: @Composable (() -> Unit)? = null, - trailingIcon: @Composable (() -> Unit)? = null -) diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/lazy/ext/LazyDsl.kt b/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/lazy/ext/LazyDsl.kt deleted file mode 100644 index a58aecf1..00000000 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/lazy/ext/LazyDsl.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.huanshankeji.compose.material.lazy.ext - -//import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.runtime.Composable -import com.huanshankeji.compose.ui.Modifier - -/** @see LazyListScope */ -expect class LazyListScope { - fun item(key: Any? = null, contentType: Any? = null, content: @Composable LazyItemScope.() -> Unit) - - fun items( - count: Int, - key: ((index: Int) -> Any)? = null, - contentType: (index: Int) -> Any? = { null }, - itemContent: @Composable LazyItemScope.(index: Int) -> Unit - ) - - fun group( - key: Any? = null, - contentType: Any? = null, - headerContent: @Composable HeaderScope.() -> Unit, - content: LazyListScope.() -> Unit - ) -} - -expect class LazyItemScope /*{ - fun Modifier.fillParentMaxSize(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f): Modifier - fun Modifier.fillParentMaxWidth(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f): Modifier - fun Modifier.fillParentMaxHeight(@FloatRange(from = 0.0, to = 1.0) fraction: Float = 1f): Modifier -}*/ - -expect class HeaderScope - -/** - * The current implementation is not actually lazy on web, but it seems not necessary to be. - */ -@Composable -expect fun LazyColumn(modifier: Modifier = Modifier, content: LazyListScope.() -> Unit) - -/** - * An alias for [LazyColumn] that is more conventional for Web/HTML/JS. - */ -@Composable -fun ScrollableList(modifier: Modifier = Modifier, content: LazyListScope.() -> Unit) = - LazyColumn(modifier, content) diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Icon.js.kt b/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Icon.js.kt deleted file mode 100644 index 1bbb9399..00000000 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Icon.js.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.huanshankeji.compose.material - -import androidx.compose.runtime.Composable -import com.huanshankeji.compose.material.icons.Icon -import com.huanshankeji.compose.ui.Modifier -import com.varabyte.kobweb.compose.ui.toAttrs -import dev.petuska.kmdcx.icons.MDCIconSpan -import org.jetbrains.compose.web.attributes.AttrsScope - -/** - * There is often a better alternative of adding the CSS rule to the parent element to using this composable directly. - */ -@Composable -actual fun Icon(icon: Icon, contentDescription: String?, modifier: Modifier) = - MDCIconSpan(icon.mdcIcon, attrs = modifier.platformModifier.toAttrs { - contentDescription(contentDescription) - }) - -internal fun AttrsScope<*>.contentDescription(contentDescription: String?) = - contentDescription?.let { - // aria-label's semantics don't seem to meet the requirements perfectly because it labels an interactive element - // see: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label, https://stackoverflow.com/questions/22039910/what-is-aria-label-and-how-should-i-use-it - //attr("aria-label", it) - - title(it) - } diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/TextField.js.kt b/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/TextField.js.kt deleted file mode 100644 index 3be1a56f..00000000 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/TextField.js.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.huanshankeji.compose.material - -import androidx.compose.runtime.Composable -import com.huanshankeji.compose.ui.Modifier -import com.varabyte.kobweb.compose.ui.toAttrs -import dev.petuska.kmdc.textfield.MDCTextField - -@Composable -actual fun TextField( - value: String, - onValueChange: (String) -> Unit, - modifier: Modifier, - enabled: Boolean, - label: String?, - leadingIcon: @Composable (() -> Unit)?, - trailingIcon: @Composable (() -> Unit)?, -) = - MDCTextField(value, - attrs = modifier.platformModifier.toAttrs { - onInput { onValueChange(it.value) } - }, - disabled = !enabled, - label = label, - leadingIcon = leadingIcon?.let { { it() } }, - trailingIcon = trailingIcon?.let { { it() } } - ) diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/Icons.js.kt b/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/Icons.js.kt deleted file mode 100644 index 35c026f5..00000000 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/Icons.js.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.huanshankeji.compose.material.icons - -import dev.petuska.kmdcx.icons.MDCIcon - -actual class Icon(val mdcIcon: MDCIcon) diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.js.kt b/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.js.kt deleted file mode 100644 index dfb95d7b..00000000 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/icons/filled/Icons.js.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.huanshankeji.compose.material.icons.filled - -import com.huanshankeji.compose.material.icons.Icon -import com.huanshankeji.compose.material.icons.Icons -import dev.petuska.kmdcx.icons.MDCIcon - -actual val Icons.Filled.Add: Icon get() = Icon(MDCIcon.Add) -actual val Icons.Filled.Menu: Icon get() = Icon(MDCIcon.Menu) -actual val Icons.Filled.Search: Icon get() = Icon(MDCIcon.Search) diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/lazy/ext/LazyDsl.js.kt b/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/lazy/ext/LazyDsl.js.kt deleted file mode 100644 index dad37eb8..00000000 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/lazy/ext/LazyDsl.js.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.huanshankeji.compose.material.lazy.ext - -import androidx.compose.runtime.Composable -import com.huanshankeji.compose.ui.Modifier -import com.varabyte.kobweb.compose.ui.toAttrs -import dev.petuska.kmdc.list.MDCList -import dev.petuska.kmdc.list.MDCListGroup -import dev.petuska.kmdc.list.MDCListScope -import dev.petuska.kmdc.list.Subheader -import dev.petuska.kmdc.list.item.ListItem -import dev.petuska.kmdc.list.item.MDCListItemScope -import org.jetbrains.compose.web.css.* -import org.jetbrains.compose.web.dom.ElementScope -import org.w3c.dom.HTMLHeadingElement -import org.w3c.dom.HTMLLIElement -import org.w3c.dom.HTMLUListElement - -/** - * This class contains mutable fields. - * @see androidx.compose.foundation.lazy.LazyListScopeImpl - */ -actual class LazyListScope( - val mdcListScope: MDCListScope -) { - private var composables: MutableList<@Composable () -> Unit>? = null - private fun addComposable(composable: @Composable () -> Unit) { - composables!!.add(composable) - } - - /** Add the composable functions with the non-composable functions and then invoke them. */ - @Composable - fun ComposableRun(content: LazyListScope.() -> Unit) { - composables = mutableListOf() - content() - for (composable in composables!!) - composable() - composables = null - } - - - actual fun item(key: Any?, contentType: Any?, content: @Composable LazyItemScope.() -> Unit) = addComposable { - mdcListScope.ListItem { LazyItemScope(this).content() } - } - - actual fun items( - count: Int, - key: ((index: Int) -> Any)?, - contentType: (index: Int) -> Any?, - itemContent: @Composable LazyItemScope.(index: Int) -> Unit - ) = addComposable { - repeat(count) { i -> - mdcListScope.ListItem { LazyItemScope(this).itemContent(i) } - } - } - - actual fun group( - key: Any?, - contentType: Any?, - headerContent: @Composable HeaderScope.() -> Unit, - content: LazyListScope.() -> Unit - ) = addComposable { - MDCListGroup { - Subheader { - HeaderScope(this).headerContent() - } - MDCList { - LazyListScope(this).ComposableRun(content) - } - } - } -} - -actual class LazyItemScope(val mdcListItemScope: MDCListItemScope) -actual class HeaderScope(val headingElementScope: ElementScope) - -@Composable -actual fun LazyColumn(modifier: Modifier, content: LazyListScope.() -> Unit) = - MDCList(attrs = modifier.platformModifier.toAttrs { - style { - //overflowY("scroll") - overflowY("auto") - } - }) { - LazyListScope(this).ComposableRun(content) - } diff --git a/compose-multiplatform-material/build.gradle.kts b/compose-multiplatform-material2/build.gradle.kts similarity index 95% rename from compose-multiplatform-material/build.gradle.kts rename to compose-multiplatform-material2/build.gradle.kts index a9f81df9..c2781d50 100644 --- a/compose-multiplatform-material/build.gradle.kts +++ b/compose-multiplatform-material2/build.gradle.kts @@ -17,6 +17,7 @@ kotlin { api(compose.runtime) implementation("org.jetbrains.compose.annotation-internal:annotation:${DependencyVersions.composeMultiplatform}") api(project(":compose-multiplatform-common")) + api(project(":compose-multiplatform-material-icons-core")) //compileOnly(compose.material) // for KDoc element links only } } diff --git a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Button.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Button.androidxCommon.kt similarity index 55% rename from compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Button.androidxCommon.kt rename to compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Button.androidxCommon.kt index ed4b43eb..523b3632 100644 --- a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Button.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Button.androidxCommon.kt @@ -1,7 +1,8 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.foundation.layout.RowScope +import com.huanshankeji.compose.foundation.layout.toPlatformRowScopeContent import com.huanshankeji.compose.ui.Modifier @Composable @@ -10,9 +11,7 @@ actual fun Button( modifier: Modifier, content: @Composable RowScope.() -> Unit ) = - androidx.compose.material.Button(onClick, modifier.platformModifier) { - RowScope.Impl(this).content() - } + androidx.compose.material.Button(onClick, modifier.platformModifier, content = content.toPlatformRowScopeContent()) @Composable actual fun OutlinedButton( @@ -20,9 +19,11 @@ actual fun OutlinedButton( modifier: Modifier, content: @Composable RowScope.() -> Unit ) = - androidx.compose.material.OutlinedButton(onClick, modifier.platformModifier) { - RowScope.Impl(this).content() - } + androidx.compose.material.OutlinedButton( + onClick, + modifier.platformModifier, + content = content.toPlatformRowScopeContent() + ) @Composable actual fun TextButton( @@ -30,6 +31,8 @@ actual fun TextButton( modifier: Modifier, content: @Composable RowScope.() -> Unit ) = - androidx.compose.material.TextButton(onClick, modifier.platformModifier) { - RowScope.Impl(this).content() - } + androidx.compose.material.TextButton( + onClick, + modifier.platformModifier, + content = content.toPlatformRowScopeContent() + ) diff --git a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Card.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Card.androidxCommon.kt similarity index 85% rename from compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Card.androidxCommon.kt rename to compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Card.androidxCommon.kt index f7ce91f1..43743737 100644 --- a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Card.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Card.androidxCommon.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Checkbox.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Checkbox.androidxCommon.kt new file mode 100644 index 00000000..76dfdb69 --- /dev/null +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Checkbox.androidxCommon.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Checkbox( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier, + enabled: Boolean +) = + androidx.compose.material.Checkbox(checked, onCheckedChange, modifier.platformModifier, enabled) diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Divider.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Divider.androidxCommon.kt new file mode 100644 index 00000000..cde8267c --- /dev/null +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Divider.androidxCommon.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Divider(modifier: Modifier) = + androidx.compose.material.Divider(modifier.platformModifier) diff --git a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Icon.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Icon.androidxCommon.kt similarity index 88% rename from compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Icon.androidxCommon.kt rename to compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Icon.androidxCommon.kt index 96d73922..62561ab2 100644 --- a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Icon.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Icon.androidxCommon.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.material.icons.Icon diff --git a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/IconButton.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/IconButton.androidxCommon.kt similarity index 93% rename from compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/IconButton.androidxCommon.kt rename to compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/IconButton.androidxCommon.kt index f3f951ca..ad32db0a 100644 --- a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/IconButton.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/IconButton.androidxCommon.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Switch.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Switch.androidxCommon.kt new file mode 100644 index 00000000..4d7742cf --- /dev/null +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Switch.androidxCommon.kt @@ -0,0 +1,15 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +// https://developer.android.com/develop/ui/compose/components/switch + +@Composable +actual fun Switch( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier, + enabled: Boolean +) = + androidx.compose.material.Switch(checked, onCheckedChange, modifier.platformModifier, enabled) diff --git a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Text.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Text.androidxCommon.kt similarity index 83% rename from compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Text.androidxCommon.kt rename to compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Text.androidxCommon.kt index 64f372f6..a5d0c347 100644 --- a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/Text.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/Text.androidxCommon.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier diff --git a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/ext/Button.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Button.androidxCommon.kt similarity index 90% rename from compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/ext/Button.androidxCommon.kt rename to compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Button.androidxCommon.kt index 2b5ebb67..78b5cbee 100644 --- a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/ext/Button.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Button.androidxCommon.kt @@ -1,10 +1,10 @@ -package com.huanshankeji.compose.material.ext +package com.huanshankeji.compose.material2.ext import androidx.compose.foundation.layout.RowScope import androidx.compose.material.OutlinedButton import androidx.compose.material.TextButton import androidx.compose.runtime.Composable -import com.huanshankeji.compose.material.ext.ButtonType.* +import com.huanshankeji.compose.material2.ext.ButtonType.* import com.huanshankeji.compose.ui.Modifier @Suppress("NAME_SHADOWING") 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 new file mode 100644 index 00000000..514a5ff4 --- /dev/null +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.androidxCommon.kt @@ -0,0 +1,24 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.material.RadioButton +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.semantics.Role +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.PlatformModifier + +// https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#RadioButton(kotlin.Boolean,kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Boolean,androidx.compose.foundation.interaction.MutableInteractionSource,androidx.compose.material.RadioButtonColors) + +@Composable +actual fun RadioRow(selected: Boolean, label: String, onClick: () -> Unit, modifier: Modifier, enabled: Boolean) = + Row(Modifier.platformModifier.selectable(selected, enabled, Role.RadioButton, onClick)) { + RadioButton(selected, null) + Text(label) + } + +@Composable +actual fun RadioGroupRow(modifier: Modifier, content: @Composable () -> Unit) = + Row(PlatformModifier.selectableGroup().then(modifier.platformModifier)) { content() } diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Switch.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Switch.androidxCommon.kt new file mode 100644 index 00000000..857e5ef5 --- /dev/null +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Switch.androidxCommon.kt @@ -0,0 +1,20 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.Row +import com.huanshankeji.compose.material2.Switch +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun SwitchWithLabel( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + label: String, + modifier: Modifier, + enabled: Boolean +) = + Row(modifier) { + Switch(checked, onCheckedChange, enabled = enabled) + Text(label) + } diff --git a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/ext/Text.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.androidxCommon.kt similarity index 75% rename from compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/ext/Text.androidxCommon.kt rename to compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.androidxCommon.kt index d19e1b1e..e7c8016b 100644 --- a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/ext/Text.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.androidxCommon.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material.ext +package com.huanshankeji.compose.material2.ext import androidx.compose.material.Text import androidx.compose.runtime.Composable diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.androidxCommon.kt new file mode 100644 index 00000000..f21bd824 --- /dev/null +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.androidxCommon.kt @@ -0,0 +1,135 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.text.KeyboardActions +import com.huanshankeji.compose.foundation.text.KeyboardOptions +import com.huanshankeji.compose.foundation.text.toPlatformValue +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material2.Icon +import com.huanshankeji.compose.material2.Text +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun TextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: @Composable (() -> Unit)?, + trailingIcon: @Composable (() -> Unit)?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean +) = + androidx.compose.material.TextField( + value, + onValueChange, + modifier.platformModifier, + enabled = enabled, + label = label?.let { { Text(it) } }, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + keyboardOptions = keyboardOptions.toPlatformValue(), + keyboardActions = keyboardActions.toPlatformValue(), + singleLine = singleLine + ) + +@Composable +actual fun TextFieldWithMaterialIcons( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: Icon?, + trailingIcon: Icon?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean +) = + TextField( + value, + onValueChange, + modifier, + enabled, + label, + leadingIcon?.let { { Icon(it, null) } }, + trailingIcon?.let { { Icon(it, null) } }, + keyboardOptions, keyboardActions, singleLine + ) + + +@Composable +actual fun OutlinedTextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: @Composable (() -> Unit)?, + trailingIcon: @Composable (() -> Unit)?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean +) = + androidx.compose.material.OutlinedTextField( + value, + onValueChange, + modifier.platformModifier, + enabled = enabled, + label = label?.let { { Text(it) } }, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + keyboardOptions = keyboardOptions.toPlatformValue(), + keyboardActions = keyboardActions.toPlatformValue(), + singleLine = singleLine + ) + +@Composable +actual fun OutlinedTextFieldWithMaterialIcons( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: Icon?, + trailingIcon: Icon?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean +) = + OutlinedTextField( + value, + onValueChange, + modifier, + enabled, + label, + leadingIcon?.let { { Icon(it, null) } }, + trailingIcon?.let { { Icon(it, null) } }, + keyboardOptions, keyboardActions, singleLine + ) + + +@Composable +actual fun TextArea( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + lines: Int +) = + androidx.compose.material.TextField( + value, + onValueChange, + modifier.platformModifier, + enabled = enabled, + label = label?.let { { Text(it) } }, + keyboardOptions = keyboardOptions.toPlatformValue(), + keyboardActions = keyboardActions.toPlatformValue(), + maxLines = lines, + minLines = lines + ) diff --git a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/ext/TopAppBarScaffold.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.androidxCommon.kt similarity index 91% rename from compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/ext/TopAppBarScaffold.androidxCommon.kt rename to compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.androidxCommon.kt index a5862dae..4f47c58c 100644 --- a/compose-multiplatform-material/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material/ext/TopAppBarScaffold.androidxCommon.kt +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.androidxCommon.kt @@ -1,12 +1,12 @@ -package com.huanshankeji.compose.material.ext +package com.huanshankeji.compose.material2.ext 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.material.Icon -import com.huanshankeji.compose.material.IconButton 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 actual class NavigationIconScope private constructor() { diff --git a/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.androidxCommon.kt b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.androidxCommon.kt new file mode 100644 index 00000000..7a77b97d --- /dev/null +++ b/compose-multiplatform-material2/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.androidxCommon.kt @@ -0,0 +1,50 @@ +package com.huanshankeji.compose.material2.lazy.ext + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.ListItem +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier +import androidx.compose.foundation.lazy.LazyItemScope as PlatformLazyItemScope +import androidx.compose.foundation.lazy.LazyListScope as PlatformLazyListScope + +actual class ListScope(val platformValue: PlatformLazyListScope) { + actual fun item(key: Any?, contentType: Any?, content: @Composable ItemScope.() -> Unit) = + platformValue.item(key, contentType) { ItemScope(this).content() } + + actual fun items( + count: Int, + key: ((index: Int) -> Any)?, + contentType: (index: Int) -> Any?, + itemContent: @Composable ItemScope.(index: Int) -> Unit + ) = + platformValue.items(count, key, contentType) { ItemScope(this).itemContent(it) } + + actual fun group( + key: Any?, + contentType: Any?, + headerContent: @Composable HeaderScope.() -> Unit, + content: ListScope.() -> Unit + ) { + @OptIn(ExperimentalFoundationApi::class) + platformValue.stickyHeader(key, contentType) { HeaderScope(this).headerContent() } + content() + } + + @Composable + actual fun ItemScope.ListItemContent(listItemComponents: ListItemComponents) = + with(listItemComponents) { + @OptIn(ExperimentalMaterialApi::class) + ListItem(secondaryText = secondaryText, text = text) + } +} + +actual class ItemScope(val platformValue: PlatformLazyItemScope) +actual typealias HeaderScope = ItemScope + +@Composable +actual fun List(modifier: Modifier, content: ListScope.() -> Unit) = + // Note that it's actually in the `foundation` package. + androidx.compose.foundation.lazy.LazyColumn(modifier.platformModifier) { + ListScope(this).content() + } diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Button.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Button.kt similarity index 93% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Button.kt rename to compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Button.kt index 30aa67ec..51b23c43 100644 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Button.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Button.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.foundation.layout.RowScope diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Card.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Card.kt similarity index 88% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Card.kt rename to compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Card.kt index 6ce99118..16fa783a 100644 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Card.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Card.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Checkbox.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Checkbox.kt new file mode 100644 index 00000000..394e81e9 --- /dev/null +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Checkbox.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun Checkbox( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + enabled: Boolean = true +) diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Divider.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Divider.kt new file mode 100644 index 00000000..63453cfd --- /dev/null +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Divider.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun Divider( + modifier: Modifier = Modifier, + //color: Color, + //thickness: Dp = 1.dp, + //startIndent: Dp = 0.dp +) diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Icon.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Icon.kt similarity index 85% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Icon.kt rename to compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Icon.kt index 6d2aff85..da4f8dc2 100644 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Icon.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Icon.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.material.icons.Icon diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/IconButton.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/IconButton.kt similarity index 90% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/IconButton.kt rename to compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/IconButton.kt index 5ef6c141..892ad67b 100644 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/IconButton.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/IconButton.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Switch.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Switch.kt new file mode 100644 index 00000000..d7f20683 --- /dev/null +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Switch.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun Switch( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + enabled: Boolean = true +) diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Text.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Text.kt similarity index 71% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Text.kt rename to compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Text.kt index 1de5ceef..7fc56d92 100644 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/Text.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/Text.kt @@ -1,6 +1,7 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material2.ext.InlineText import com.huanshankeji.compose.ui.Modifier /** @@ -8,6 +9,7 @@ import com.huanshankeji.compose.ui.Modifier * 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 */ @Composable diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/Button.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Button.kt similarity index 89% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/Button.kt rename to compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Button.kt index cd088f72..5f738e6e 100644 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/Button.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Button.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material.ext +package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/IconButton.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.kt similarity index 58% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/IconButton.kt rename to compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.kt index 3cb78cb3..551af428 100644 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/IconButton.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/IconButton.kt @@ -1,8 +1,8 @@ -package com.huanshankeji.compose.material.ext +package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable -import com.huanshankeji.compose.material.Icon import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material2.Icon import com.huanshankeji.compose.ui.Modifier @Composable @@ -12,4 +12,4 @@ fun IconButton( icon: Icon, contentDescription: String? ) = - com.huanshankeji.compose.material.IconButton(onClick, modifier) { Icon(icon, contentDescription) } + 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 new file mode 100644 index 00000000..2f2b1d47 --- /dev/null +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.kt @@ -0,0 +1,19 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +// TODO colors +@Composable +expect fun RadioRow( + selected: Boolean, + label: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true +) + +@Composable +expect fun RadioGroupRow(modifier: Modifier = Modifier, content: @Composable () -> Unit) + +// TODO `RadioGroupColumn` diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Switch.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Switch.kt new file mode 100644 index 00000000..2e2adc01 --- /dev/null +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Switch.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun SwitchWithLabel( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + label: String, + modifier: Modifier = Modifier, + enabled: Boolean = true +) diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/Text.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.kt similarity index 82% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/Text.kt rename to compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.kt index 097dc7de..97589382 100644 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/Text.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/Text.kt @@ -1,7 +1,7 @@ -package com.huanshankeji.compose.material.ext +package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable -import com.huanshankeji.compose.material.Text +import com.huanshankeji.compose.material2.Text import com.huanshankeji.compose.ui.Modifier /** diff --git a/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.kt new file mode 100644 index 00000000..0155f69f --- /dev/null +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.kt @@ -0,0 +1,95 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.text.KeyboardActions +import com.huanshankeji.compose.foundation.text.KeyboardOptions +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.ui.Modifier + +/** + * @param singleLine on JS it's always single line. + */ +@Composable +expect fun TextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + label: String? = null, + /* + // These 2 are not the same thing. + placeholder: String? = null, + helperText: String? = null, + */ + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = false +) + +/** + * @see TextField + */ +@Composable +expect fun TextFieldWithMaterialIcons( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + label: String? = null, + leadingIcon: Icon? = null, + trailingIcon: Icon? = null, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = false +) + + +// maybe put in `OutlinedTextField.kt` +/** + * @param singleLine on JS it's always single line. + */ +@Composable +expect fun OutlinedTextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + label: String? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = false +) + +/** + * @see OutlinedTextField + */ +@Composable +expect fun OutlinedTextFieldWithMaterialIcons( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + label: String? = null, + leadingIcon: Icon? = null, + trailingIcon: Icon? = null, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = false +) + + +@Composable +expect fun TextArea( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + label: String? = null, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + lines: Int +) diff --git a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/TopAppBarScaffold.kt b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.kt similarity index 95% rename from compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/TopAppBarScaffold.kt rename to compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.kt index 12edd3d3..13f9c465 100644 --- a/compose-multiplatform-material/src/commonMain/kotlin/com/huanshankeji/compose/material/ext/TopAppBarScaffold.kt +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material.ext +package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable import com.huanshankeji.compose.material.icons.Icon 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 new file mode 100644 index 00000000..e16d310a --- /dev/null +++ b/compose-multiplatform-material2/src/commonMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.kt @@ -0,0 +1,70 @@ +package com.huanshankeji.compose.material2.lazy.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material2.ext.InlineText +import com.huanshankeji.compose.ui.Modifier + +expect class ListScope { + fun item(key: Any? = null, contentType: Any? = null, content: @Composable ItemScope.() -> Unit) + + fun items( + count: Int, + key: ((index: Int) -> Any)? = null, + contentType: (index: Int) -> Any? = { null }, + itemContent: @Composable ItemScope.(index: Int) -> Unit + ) + + fun group( + key: Any? = null, + contentType: Any? = null, + headerContent: @Composable HeaderScope.() -> Unit, + content: ListScope.() -> Unit + ) + + @Composable + fun ItemScope.ListItemContent(listItemComponents: ListItemComponents) +} + +expect class ItemScope + +expect class HeaderScope + +class ListItemComponents( + val text: @Composable () -> Unit, + val secondaryText: @Composable (() -> Unit)? = null +) { + constructor(text: String, secondaryText: String? = null) : this( + { InlineText(text) }, + secondaryText?.let { { InlineText(it) } } + ) +} + +// see https://youtrack.jetbrains.com/issue/KT-20427 + +fun ListScope.conventionalItem(key: Any? = null, contentType: Any? = null, content: ListItemComponents) = + item(key, contentType) { + ListItemContent(content) + } + +fun ListScope.conventionalItems( + count: Int, + key: ((index: Int) -> Any)? = null, + contentType: (index: Int) -> Any? = { null }, + itemContent: (index: Int) -> ListItemComponents +) = + items(count, key, contentType) { index -> + ListItemContent(itemContent(index)) + } + +/** + * The current implementation is not actually lazy on web, but it seems not necessary to be. + */ +@Composable +expect fun List(modifier: Modifier = Modifier, content: ListScope.() -> Unit) + +/** + * An alias for [List] that is more similar to `androidx.compose.foundation.layout.LazyColumn`. + */ +@Composable +fun LazyColumnList(modifier: Modifier = Modifier, content: ListScope.() -> Unit) = + List(modifier, content) diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Button.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Button.js.kt similarity index 61% rename from compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Button.js.kt rename to compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Button.js.kt index 562497ce..a131af88 100644 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Button.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Button.js.kt @@ -1,13 +1,30 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.foundation.layout.Row import com.huanshankeji.compose.foundation.layout.RowScope -import com.huanshankeji.compose.material.ext.ButtonType -import com.huanshankeji.compose.material.ext.toMDCButtonType +import com.huanshankeji.compose.material2.ext.ButtonType +import com.huanshankeji.compose.material2.ext.toMDCButtonType import com.huanshankeji.compose.ui.Modifier -import com.varabyte.kobweb.compose.ui.toAttrs +import com.huanshankeji.compose.ui.toAttrs import dev.petuska.kmdc.button.MDCButton +import dev.petuska.kmdc.button.MDCButtonScope +import org.w3c.dom.HTMLButtonElement + +@Composable +internal fun CommonButton( + onClick: () -> Unit, + buttonType: ButtonType, + modifier: Modifier, + content: @Composable MDCButtonScope.() -> Unit +) = + MDCButton( + buttonType.toMDCButtonType(), + attrs = modifier.toAttrs { + onClick { onClick() } + }, + content = content + ) @Composable private fun Button( @@ -16,10 +33,7 @@ private fun Button( modifier: Modifier, content: @Composable RowScope.() -> Unit ) = - MDCButton(buttonType.toMDCButtonType(), - attrs = modifier.platformModifier.toAttrs { - onClick { onClick() } - }) { + CommonButton(onClick, buttonType, modifier) { Row(content = content) } diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Card.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Card.js.kt similarity index 79% rename from compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Card.js.kt rename to compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Card.js.kt index 2f60b7a3..60e0a6c5 100644 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Card.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Card.js.kt @@ -1,11 +1,11 @@ -package com.huanshankeji.compose.material +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.varabyte.kobweb.compose.ui.toAttrs import dev.petuska.kmdc.card.MDCCard -import com.varabyte.kobweb.compose.ui.Modifier as PlatformModifier @Composable actual fun Card(modifier: Modifier, content: @Composable () -> Unit) = diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Checkbox.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Checkbox.js.kt new file mode 100644 index 00000000..dd84f269 --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Checkbox.js.kt @@ -0,0 +1,18 @@ +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.checkbox.MDCCheckbox + +// https://github.com/mpetuska/kmdc/blob/master/sandbox/src/jsMain/showcases/MDCCheckbox.kt +@Composable +actual fun Checkbox( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier, + enabled: Boolean +) = + MDCCheckbox(checked, !enabled, attrs = modifier.toAttrs { + onCheckedChange?.let { onInput { it(!checked) } } + }) diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Divider.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Divider.js.kt new file mode 100644 index 00000000..f610f20c --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Divider.js.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs +import org.jetbrains.compose.web.dom.Hr + +// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr +// see: https://github.com/varabyte/kobweb/blob/main/frontend/silk-widgets/src/jsMain/kotlin/com/varabyte/kobweb/silk/components/layout/Divider.kt +// see: https://github.com/mpetuska/kmdc/blob/master/katalog/katalog-runtime/src/jsMain/kotlin/layout/Divider.kt +@Composable +actual fun Divider(modifier: Modifier) = + Hr(modifier.toAttrs()) diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Icon.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Icon.js.kt new file mode 100644 index 00000000..d580c98e --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Icon.js.kt @@ -0,0 +1,18 @@ +package com.huanshankeji.compose.material2 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.contentDescription +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material2.icons.toMDCIcon +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs +import dev.petuska.kmdcx.icons.MDCIconSpan + +/** + * There is often a better alternative of adding the CSS rule to the parent element to using this composable directly. + */ +@Composable +actual fun Icon(icon: Icon, contentDescription: String?, modifier: Modifier) = + MDCIconSpan(icon.toMDCIcon(), attrs = modifier.toAttrs { + contentDescription(contentDescription) + }) diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/IconButton.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/IconButton.js.kt similarity index 78% rename from compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/IconButton.js.kt rename to compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/IconButton.js.kt index e38d353a..be0b2244 100644 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/IconButton.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/IconButton.js.kt @@ -1,8 +1,8 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.ui.Modifier -import com.varabyte.kobweb.compose.ui.toAttrs +import com.huanshankeji.compose.ui.toAttrs import dev.petuska.kmdc.icon.button.MDCIconButton @Composable @@ -11,7 +11,7 @@ actual fun IconButton( modifier: Modifier, content: @Composable () -> Unit ) = - MDCIconButton(attrs = modifier.platformModifier.toAttrs { + MDCIconButton(attrs = modifier.toAttrs { onClick { onClick() } }) { content() } diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Switch.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Switch.js.kt new file mode 100644 index 00000000..bd1dcdb1 --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Switch.js.kt @@ -0,0 +1,31 @@ +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.switch.MDCSwitch +import org.jetbrains.compose.web.attributes.disabled + +// https://github.com/mpetuska/kmdc/blob/master/sandbox/src/jsMain/showcases/MDCSwitch.kt + +@Composable +internal fun CommonSwitch( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + label: String?, + modifier: Modifier, + enabled: Boolean +) = + MDCSwitch(checked, label, modifier.toAttrs { + if (!enabled) disabled() + onCheckedChange?.let { onClick { it(!checked) } } + }) + +@Composable +actual fun Switch( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier, + enabled: Boolean +) = + CommonSwitch(checked, onCheckedChange, null, modifier, enabled) diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Text.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Text.js.kt similarity index 84% rename from compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Text.js.kt rename to compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Text.js.kt index 334a347b..a3733ad5 100644 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/Text.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/Text.js.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material +package com.huanshankeji.compose.material2 import androidx.compose.runtime.Composable import com.huanshankeji.compose.foundation.text.BasicText diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/ext/Button.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Button.js.kt similarity index 70% rename from compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/ext/Button.js.kt rename to compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Button.js.kt index 086fa5bd..baca4925 100644 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/ext/Button.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Button.js.kt @@ -1,11 +1,10 @@ -package com.huanshankeji.compose.material.ext +package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable -import com.huanshankeji.compose.material.ext.ButtonType.* +import com.huanshankeji.compose.material2.CommonButton +import com.huanshankeji.compose.material2.ext.ButtonType.* import com.huanshankeji.compose.ui.Modifier -import com.varabyte.kobweb.compose.ui.toAttrs import dev.petuska.kmdc.button.Label -import dev.petuska.kmdc.button.MDCButton import dev.petuska.kmdc.button.MDCButtonScope import dev.petuska.kmdc.button.MDCButtonType import org.w3c.dom.HTMLButtonElement @@ -17,10 +16,7 @@ actual fun Button( modifier: Modifier, content: @Composable ButtonScope.() -> Unit ) = - MDCButton(buttonType.toMDCButtonType(), - attrs = modifier.platformModifier.toAttrs { - onClick { onClick() } - }) { + CommonButton(onClick, buttonType, modifier) { ButtonScope(this).content() } 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 new file mode 100644 index 00000000..8dd668f7 --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Radio.js.kt @@ -0,0 +1,21 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.runtime.Composable +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 + +// 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() } + }) + +@Composable +actual fun RadioGroupRow(modifier: Modifier, content: @Composable () -> Unit) = + MDCFormField(attrs = modifier.toAttrs()) { + content() + } diff --git a/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Switch.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Switch.js.kt new file mode 100644 index 00000000..953de130 --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Switch.js.kt @@ -0,0 +1,15 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material2.CommonSwitch +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun SwitchWithLabel( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + label: String, + modifier: Modifier, + enabled: Boolean +) = + CommonSwitch(checked, onCheckedChange, label, modifier, enabled) \ No newline at end of file diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/ext/Text.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Text.js.kt similarity index 76% rename from compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/ext/Text.js.kt rename to compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Text.js.kt index a9cc6bb2..85e471c1 100644 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/ext/Text.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/Text.js.kt @@ -1,4 +1,4 @@ -package com.huanshankeji.compose.material.ext +package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable import org.jetbrains.compose.web.dom.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 new file mode 100644 index 00000000..57792107 --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TextField.js.kt @@ -0,0 +1,225 @@ +package com.huanshankeji.compose.material2.ext + +import androidx.compose.runtime.Composable +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.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs +import dev.petuska.kmdc.core.MDCContent +import dev.petuska.kmdc.textfield.MDCTextArea +import dev.petuska.kmdc.textfield.MDCTextField +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 +fun CommonTextFieldWithMDCContentIcons( + value: String, + type: MDCTextFieldType, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: MDCContent? = null, + trailingIcon: MDCContent? = null, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions +) = + MDCTextField( + value, + type, + !enabled, + label, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + attrs = modifier.toAttrs { + onInput { onValueChange(it.value) } + + attrsFrom(keyboardOptions, keyboardActions) + } + ) + +@Composable +fun CommonTextFieldWithIconsLikeAndroidx( + value: String, + type: MDCTextFieldType, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: @Composable (() -> Unit)?, + trailingIcon: @Composable (() -> Unit)?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions +) = + CommonTextFieldWithMDCContentIcons( + value, + type, + onValueChange, + modifier, + enabled, + label, + leadingIcon?.let { { it() } }, + trailingIcon?.let { { it() } }, + keyboardOptions, + keyboardActions + ) + +@Composable +fun CommonTextFieldWithMaterialIcons( + value: String, + type: MDCTextFieldType, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: Icon?, + trailingIcon: Icon?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions +) = + CommonTextFieldWithMDCContentIcons( + value, + type, + onValueChange, + modifier, + 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) } } }, + keyboardOptions, + keyboardActions + ) + + +@Composable +actual fun TextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: @Composable (() -> Unit)?, + trailingIcon: @Composable (() -> Unit)?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean +) = + CommonTextFieldWithIconsLikeAndroidx( + value, + MDCTextFieldType.Filled, + onValueChange, + modifier, + enabled, + label, + leadingIcon, + trailingIcon, + keyboardOptions, + keyboardActions + ) + +@Composable +actual fun TextFieldWithMaterialIcons( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: Icon?, + trailingIcon: Icon?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean +) = + CommonTextFieldWithMaterialIcons( + value, + MDCTextFieldType.Filled, + onValueChange, + modifier, + enabled, + label, + leadingIcon, + trailingIcon, + keyboardOptions, + keyboardActions + ) + + +@Composable +actual fun OutlinedTextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: @Composable (() -> Unit)?, + trailingIcon: @Composable (() -> Unit)?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean +) = + CommonTextFieldWithIconsLikeAndroidx( + value, + MDCTextFieldType.Outlined, + onValueChange, + modifier, + enabled, + label, + leadingIcon, + trailingIcon, + keyboardOptions, + keyboardActions + ) + +@Composable +actual fun OutlinedTextFieldWithMaterialIcons( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + leadingIcon: Icon?, + trailingIcon: Icon?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean +) = + CommonTextFieldWithMaterialIcons( + value, + MDCTextFieldType.Outlined, + onValueChange, + modifier, + enabled, + label, + leadingIcon, + trailingIcon, + keyboardOptions, + keyboardActions + ) + + +@Composable +actual fun TextArea( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + lines: Int +) = + MDCTextArea( + value, + disabled = !enabled, + label = label, + rows = lines.toUInt(), + attrs = modifier.toAttrs { + onInput { onValueChange(it.value) } + }) diff --git a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/ext/TopAppBarScaffold.js.kt b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.js.kt similarity index 88% rename from compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/ext/TopAppBarScaffold.js.kt rename to compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.js.kt index 6a790942..d3befae4 100644 --- a/compose-multiplatform-material/src/jsMain/kotlin/com/huanshankeji/compose/material/ext/TopAppBarScaffold.js.kt +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/ext/TopAppBarScaffold.js.kt @@ -1,10 +1,10 @@ -package com.huanshankeji.compose.material.ext +package com.huanshankeji.compose.material2.ext import androidx.compose.runtime.Composable -import com.huanshankeji.compose.material.contentDescription +import com.huanshankeji.compose.contentDescription import com.huanshankeji.compose.material.icons.Icon import com.huanshankeji.compose.ui.Modifier -import com.varabyte.kobweb.compose.ui.toAttrs +import com.huanshankeji.compose.ui.toAttrs import dev.petuska.kmdc.top.app.bar.* import dev.petuska.kmdcx.icons.mdcIcon import org.jetbrains.compose.web.dom.Text @@ -19,7 +19,7 @@ actual class NavigationIconScope(val mdcTopAppBarSectionScope: MDCTopAppBarSecti mdcTopAppBarSectionScope.NavButton(attrs = { mdcIcon() contentDescription(contentDescription) - }) { Text(icon.mdcIcon.type) } + }) { Text(icon.name) } } actual class TopAppBarActionsScope(val mdcTopAppBarSectionScope: MDCTopAppBarSectionScope) { @@ -32,7 +32,7 @@ actual class TopAppBarActionsScope(val mdcTopAppBarSectionScope: MDCTopAppBarSec mdcTopAppBarSectionScope.ActionButton(attrs = { mdcIcon() contentDescription(contentDescription) - }) { Text(icon.mdcIcon.type) } + }) { Text(icon.name) } } @Composable @@ -44,7 +44,7 @@ actual fun TopAppBarScaffold( content: @Composable () -> Unit ) = MDCTopAppBar { - TopAppBar(topAppBarModifier.platformModifier.toAttrs()) { + TopAppBar(topAppBarModifier.toAttrs()) { Row { Section(align = MDCTopAppBarSectionAlign.Start) { navigationIcon?.let { NavigationIconScope(this@Section).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 new file mode 100644 index 00000000..bd2a5b71 --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/icons/IconsMdc.kt @@ -0,0 +1,9 @@ +package com.huanshankeji.compose.material2.icons + +import com.huanshankeji.compose.material.icons.Icon +import dev.petuska.kmdcx.icons.MDCIcon + +val mdcIconMap = MDCIcon.entries.associateBy { it.type } + +fun Icon.toMDCIcon() = + mdcIconMap.getValue(name) 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 new file mode 100644 index 00000000..522c9da3 --- /dev/null +++ b/compose-multiplatform-material2/src/jsMain/kotlin/com/huanshankeji/compose/material2/lazy/ext/LazyDsl.js.kt @@ -0,0 +1,83 @@ +package com.huanshankeji.compose.material2.lazy.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.verticalScrollPlatformModifier +import com.huanshankeji.compose.runtime.DeferredComposableRunner +import com.huanshankeji.compose.ui.Modifier +import com.varabyte.kobweb.compose.ui.toAttrs +import dev.petuska.kmdc.list.MDCList +import dev.petuska.kmdc.list.MDCListGroup +import dev.petuska.kmdc.list.MDCListScope +import dev.petuska.kmdc.list.Subheader +import dev.petuska.kmdc.list.item.* +import org.jetbrains.compose.web.css.* +import org.jetbrains.compose.web.dom.ElementScope +import org.w3c.dom.HTMLHeadingElement +import org.w3c.dom.HTMLLIElement +import org.w3c.dom.HTMLUListElement + +/** + * This class contains mutable fields. + * @see androidx.compose.foundation.lazy.LazyListScopeImpl + */ +actual class ListScope( + val mdcListScope: MDCListScope +) { + private val deferredComposableRunner = DeferredComposableRunner() + + private fun addComposable(composable: @Composable () -> Unit) = + deferredComposableRunner.addComposable(composable) + + @Composable + internal fun ComposableRun(content: ListScope.() -> Unit) = + deferredComposableRunner.ComposableRun { content() } + + actual fun item(key: Any?, contentType: Any?, content: @Composable ItemScope.() -> Unit) = addComposable { + mdcListScope.ListItem { ItemScope(this).content() } + } + + actual fun items( + count: Int, + key: ((index: Int) -> Any)?, + contentType: (index: Int) -> Any?, + itemContent: @Composable ItemScope.(index: Int) -> Unit + ) = addComposable { + repeat(count) { i -> + mdcListScope.ListItem { ItemScope(this).itemContent(i) } + } + } + + actual fun group( + key: Any?, + contentType: Any?, + headerContent: @Composable HeaderScope.() -> Unit, + content: ListScope.() -> Unit + ) = deferredComposableRunner.addComposable { + MDCListGroup { + Subheader { + HeaderScope(this).headerContent() + } + MDCList { + ListScope(this).ComposableRun(content) + } + } + } + + @Composable + actual fun ItemScope.ListItemContent(listItemComponents: ListItemComponents) = + with(listItemComponents) { + mdcListItemScope.Label { + Primary { text() } + secondaryText?.let { Secondary { it() } } + } + } +} + +actual class ItemScope(val mdcListItemScope: MDCListItemScope) +actual class HeaderScope(val headingElementScope: ElementScope) + +@Composable +actual fun List(modifier: Modifier, content: ListScope.() -> Unit) = + MDCList(attrs = verticalScrollPlatformModifier.then(modifier.platformModifier).toAttrs()) { + ListScope(this).ComposableRun(content) + } diff --git a/compose-multiplatform-material3/build.gradle.kts b/compose-multiplatform-material3/build.gradle.kts new file mode 100644 index 00000000..9ae453fa --- /dev/null +++ b/compose-multiplatform-material3/build.gradle.kts @@ -0,0 +1,45 @@ +import com.huanshankeji.team.`Shreck Ye` +import com.huanshankeji.team.pomForTeamDefaultOpenSource + +plugins { + `lib-conventions` +} + +kotlin { + sourceSets { + /* + Use `api`. See: + https://github.com/JetBrains/compose-multiplatform-core/blob/jb-main/compose/material3/material3/build.gradle + https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/compose/material3/material3/build.gradle + */ + commonMain { + dependencies { + api(compose.runtime) + api(project(":compose-multiplatform-common")) + api(project(":compose-multiplatform-material-icons-core")) + //compileOnly(compose.material) // for KDoc element links only + } + } + androidxCommonMain { + dependencies { + api(compose.material3) + } + } + jsMain { + dependencies { + api("com.huanshankeji:compose-html-material3:${DependencyVersions.huanshankejiComposeHtml}") + implementation("com.huanshankeji:compose-html-common:${DependencyVersions.huanshankejiComposeHtml}") + } + } + } +} + +publishing.publications.withType { + pomForTeamDefaultOpenSource( + project, + "Compose Multiplatform Material 3 wrappers", + "Compose Multiplatform Material Design 3 component wrappers for `androidx.compose` and Compose HTML" + ) { + `Shreck Ye`() + } +} diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Button.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Button.androidxCommon.kt new file mode 100644 index 00000000..13821ed9 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Button.androidxCommon.kt @@ -0,0 +1,61 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.RowScope +import com.huanshankeji.compose.foundation.layout.toPlatformRowScopeContent +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Button( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable RowScope.() -> Unit +) = + androidx.compose.material3.Button( + onClick, modifier.platformModifier, enabled, content = content.toPlatformRowScopeContent() + ) + +@Composable +actual fun ElevatedButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable RowScope.() -> Unit +) = + androidx.compose.material3.ElevatedButton( + onClick, modifier.platformModifier, enabled, content = content.toPlatformRowScopeContent() + ) + +@Composable +actual fun FilledTonalButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable RowScope.() -> Unit +) = + androidx.compose.material3.FilledTonalButton( + onClick, modifier.platformModifier, enabled, content = content.toPlatformRowScopeContent() + ) + +@Composable +actual fun OutlinedButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable RowScope.() -> Unit +) = + androidx.compose.material3.OutlinedButton( + onClick, modifier.platformModifier, enabled, content = content.toPlatformRowScopeContent() + ) + +@Composable +actual fun TextButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable RowScope.() -> Unit +) = + androidx.compose.material3.TextButton( + onClick, modifier.platformModifier, enabled, content = content.toPlatformRowScopeContent() + ) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Card.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Card.androidxCommon.kt new file mode 100644 index 00000000..7eb04415 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Card.androidxCommon.kt @@ -0,0 +1,18 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.ColumnScope +import com.huanshankeji.compose.foundation.layout.toCommonColumnScopeContent +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Card(modifier: Modifier, content: @Composable ColumnScope.() -> Unit) = + androidx.compose.material3.Card(modifier.platformModifier, content = content.toCommonColumnScopeContent()) + +@Composable +actual fun ElevatedCard(modifier: Modifier, content: @Composable ColumnScope.() -> Unit) = + androidx.compose.material3.ElevatedCard(modifier.platformModifier, content = content.toCommonColumnScopeContent()) + +@Composable +actual fun OutlinedCard(modifier: Modifier, content: @Composable ColumnScope.() -> Unit) = + androidx.compose.material3.OutlinedCard(modifier.platformModifier, content = content.toCommonColumnScopeContent()) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Checkbox.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Checkbox.androidxCommon.kt new file mode 100644 index 00000000..423267a6 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Checkbox.androidxCommon.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Checkbox( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier, + enabled: Boolean +) = + androidx.compose.material3.Checkbox(checked, onCheckedChange, modifier.platformModifier, enabled) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/FloatingActionButton.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/FloatingActionButton.androidxCommon.kt new file mode 100644 index 00000000..90e68ca2 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/FloatingActionButton.androidxCommon.kt @@ -0,0 +1,38 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.RowScope +import com.huanshankeji.compose.foundation.layout.toPlatformRowScopeContent +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun FloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable () -> Unit +) = + androidx.compose.material3.FloatingActionButton(onClick, modifier.platformModifier, content = content) + +@Composable +actual fun SmallFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable () -> Unit +) = + androidx.compose.material3.SmallFloatingActionButton(onClick, modifier.platformModifier, content = content) + +@Composable +actual fun LargeFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable () -> Unit +) = + androidx.compose.material3.LargeFloatingActionButton(onClick, modifier.platformModifier, content = content) + +@Composable +actual fun ExtendedFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable RowScope.() -> Unit +) = + androidx.compose.material3.ExtendedFloatingActionButton(onClick, modifier.platformModifier, content = content.toPlatformRowScopeContent()) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Icon.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Icon.androidxCommon.kt new file mode 100644 index 00000000..74372315 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Icon.androidxCommon.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Icon( + icon: Icon, + contentDescription: String?, + modifier: Modifier +) = + androidx.compose.material3.Icon(icon, contentDescription, modifier.platformModifier) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/IconButton.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/IconButton.androidxCommon.kt new file mode 100644 index 00000000..8396c9a9 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/IconButton.androidxCommon.kt @@ -0,0 +1,104 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun IconButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.IconButton(onClick, modifier.platformModifier, enabled, content = content) + +@Composable +actual fun IconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.IconToggleButton( + checked, + onCheckedChange, + modifier.platformModifier, + enabled, + content = content + ) + +@Composable +actual fun FilledIconButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.FilledIconButton(onClick, modifier.platformModifier, enabled, content = content) + +@Composable +actual fun FilledIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.FilledIconToggleButton( + checked, + onCheckedChange, + modifier.platformModifier, + enabled, + content = content + ) + +@Composable +actual fun FilledTonalIconButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.FilledTonalIconButton(onClick, modifier.platformModifier, enabled, content = content) + +@Composable +actual fun FilledTonalIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.FilledTonalIconToggleButton( + checked, + onCheckedChange, + modifier.platformModifier, + enabled, + content = content + ) + +@Composable +actual fun OutlinedIconButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.OutlinedIconButton(onClick, modifier.platformModifier, enabled, content = content) + +@Composable +actual fun OutlinedIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.OutlinedIconToggleButton( + checked, + onCheckedChange, + modifier.platformModifier, + enabled, + content = content + ) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Switch.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Switch.androidxCommon.kt new file mode 100644 index 00000000..7fc78321 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Switch.androidxCommon.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Switch( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier, + enabled: Boolean +) = + androidx.compose.material3.Switch(checked, onCheckedChange, modifier.platformModifier, enabled = enabled) 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 new file mode 100644 index 00000000..2bbe2f8e --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/Text.androidxCommon.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Text(text: String, modifier: Modifier) = + androidx.compose.material3.Text(text, modifier.platformModifier) 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 new file mode 100644 index 00000000..3e86a435 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Button.androidxCommon.kt @@ -0,0 +1,104 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.foundation.layout.RowScope +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.ui.Modifier + +@Composable +private fun (@Composable (() -> Unit)).toRowScopeContent( + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean +): @Composable RowScope.() -> Unit = + { + // see https://m3.material.io/components/buttons/specs#34dda7d9-40df-48ce-a169-1eaffe2e1835 and https://m3.material.io/components/buttons/specs#309d928e-e9ef-41dd-89fc-9bc51f78709c + + @Composable + fun Spacer() = + Spacer(androidx.compose.ui.Modifier.size(8.dp)) + + if (icon === null) + this@toRowScopeContent() + else { + @Composable + fun icon() = + icon(Modifier.size(18.dp)) + + if (isTrailingIcon) { + this@toRowScopeContent() + Spacer() + icon() + } else { + icon() + Spacer() + this@toRowScopeContent() + } + } + } + + +@Composable +actual fun Button( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean, + content: @Composable () -> Unit +) = androidx.compose.material3.Button( + onClick, modifier.platformModifier, enabled, content = content.toRowScopeContent(icon, isTrailingIcon) +) + +@Composable +actual fun ElevatedButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean, + content: @Composable () -> Unit +) = androidx.compose.material3.ElevatedButton( + onClick, modifier.platformModifier, enabled, content = content.toRowScopeContent(icon, isTrailingIcon) +) + +@Composable +actual fun FilledTonalButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.FilledTonalButton( + onClick, modifier.platformModifier, enabled, content = content.toRowScopeContent(icon, isTrailingIcon) + ) + +@Composable +actual fun OutlinedButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.OutlinedButton( + onClick, modifier.platformModifier, enabled, content = content.toRowScopeContent(icon, isTrailingIcon) + ) + +@Composable +actual fun TextButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean, + content: @Composable () -> Unit +) = + androidx.compose.material3.TextButton( + onClick, modifier.platformModifier, enabled, content = content.toRowScopeContent(icon, isTrailingIcon) + ) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Card.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Card.androidxCommon.kt new file mode 100644 index 00000000..0b887757 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Card.androidxCommon.kt @@ -0,0 +1,16 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Card(modifier: Modifier, content: @Composable () -> Unit) = + com.huanshankeji.compose.material3.Card(modifier) { content() } + +@Composable +actual fun ElevatedCard(modifier: Modifier, content: @Composable () -> Unit) = + com.huanshankeji.compose.material3.ElevatedCard(modifier) { content() } + +@Composable +actual fun OutlinedCard(modifier: Modifier, content: @Composable () -> Unit) = + com.huanshankeji.compose.material3.OutlinedCard(modifier) { content() } diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/FloatingActionButton.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/FloatingActionButton.androidxCommon.kt new file mode 100644 index 00000000..8f6c31ed --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/FloatingActionButton.androidxCommon.kt @@ -0,0 +1,42 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.toContentWithoutModifier +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun FloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable (Modifier) -> Unit +) = + com.huanshankeji.compose.material3.FloatingActionButton(onClick, modifier, content.toContentWithoutModifier()) + +@Composable +actual fun SmallFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable (Modifier) -> Unit +) = + com.huanshankeji.compose.material3.SmallFloatingActionButton(onClick, modifier, content.toContentWithoutModifier()) + +@Composable +actual fun LargeFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable (Modifier) -> Unit +) = + com.huanshankeji.compose.material3.LargeFloatingActionButton(onClick, modifier, content.toContentWithoutModifier()) + +@Composable +actual fun ExtendedFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + label: String, + content: @Composable ((Modifier) -> Unit)? +) = + androidx.compose.material3.ExtendedFloatingActionButton(onClick, modifier.platformModifier) { + content?.invoke(Modifier) + Text(label) + } diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/IconButton.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/IconButton.androidxCommon.kt new file mode 100644 index 00000000..54cf3d71 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/IconButton.androidxCommon.kt @@ -0,0 +1,80 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +private fun commonIconToggleButtonContent( + checked: Boolean, + checkedContent: @Composable (Modifier) -> Unit, + uncheckedContent: @Composable () -> Unit +): @Composable () -> Unit = { + if (checked) checkedContent(Modifier) else uncheckedContent() +} + +@Composable +actual fun IconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) = + com.huanshankeji.compose.material3.IconToggleButton( + checked, + onCheckedChange, + modifier, + enabled, + commonIconToggleButtonContent(checked, checkedContent, uncheckedContent) + ) + +@Composable +actual fun FilledIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) = + com.huanshankeji.compose.material3.FilledIconToggleButton( + checked, + onCheckedChange, + modifier, + enabled, + commonIconToggleButtonContent(checked, checkedContent, uncheckedContent) + ) + +@Composable +actual fun FilledTonalIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) = + com.huanshankeji.compose.material3.FilledTonalIconToggleButton( + checked, + onCheckedChange, + modifier, + enabled, + commonIconToggleButtonContent(checked, checkedContent, uncheckedContent) + ) + +@Composable +actual fun OutlinedIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) = + com.huanshankeji.compose.material3.OutlinedIconToggleButton( + checked, + onCheckedChange, + modifier, + enabled, + commonIconToggleButtonContent(checked, checkedContent, uncheckedContent) + ) 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 new file mode 100644 index 00000000..995b5c54 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.androidxCommon.kt @@ -0,0 +1,39 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.material3.NavigationBarItem +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.PlatformRowScope +import com.huanshankeji.compose.toContentWithoutModifier +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun NavigationBar( + modifier: Modifier, + content: @Composable NavigationBarScope.() -> Unit +) = + androidx.compose.material3.NavigationBar(modifier.platformModifier) { + NavigationBarScope(this).content() + } + +actual class NavigationBarScope(val rowScope: PlatformRowScope) + +@Composable +actual fun NavigationBarScope.NavigationBarItem( + selected: Boolean, + onClick: () -> Unit, + selectedIcon: @Composable (Modifier) -> Unit, + unselectedIcon: @Composable (Modifier) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + alwaysShowLabel: Boolean +) = + rowScope.NavigationBarItem( + selected, + onClick, + (if (selected) selectedIcon else unselectedIcon).toContentWithoutModifier(), + modifier.platformModifier, + enabled, + label.toNullableInlineText(), + 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 new file mode 100644 index 00000000..4fcc7557 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/Text.androidxCommon.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material3.Text + +@Composable +actual fun InlineText(text: String) = + Text(text) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.androidxCommon.kt new file mode 100644 index 00000000..fa10e69d --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.androidxCommon.kt @@ -0,0 +1,99 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.text.KeyboardActions +import com.huanshankeji.compose.foundation.text.KeyboardOptions +import com.huanshankeji.compose.foundation.text.toPlatformValue +import com.huanshankeji.compose.ui.Modifier + +// This function can be moved into a common file. +fun String?.ToNullableTextComposable(): @Composable (() -> Unit)? = + this?.let { { Text(it) } } + +private fun (@Composable ((Modifier) -> Unit)?).toContentWithoutModifier(): @Composable (() -> Unit)? = + this?.let { { it(Modifier) } } + + +@Composable +actual fun TextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + readOnly: Boolean, + label: String?, + placeholder: String?, + leadingIcon: @Composable ((Modifier) -> Unit)?, + trailingIcon: @Composable ((Modifier) -> Unit)?, + prefix: String?, + suffix: String?, + supportingText: String?, + isError: Boolean, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean, + lines: Int +) = + androidx.compose.material3.TextField( + value, + onValueChange, + modifier.platformModifier, + enabled, + readOnly, + label = label.ToNullableTextComposable(), + placeholder = placeholder.ToNullableTextComposable(), + leadingIcon = leadingIcon.toContentWithoutModifier(), + trailingIcon = trailingIcon.toContentWithoutModifier(), + prefix = prefix.ToNullableTextComposable(), + suffix = suffix.ToNullableTextComposable(), + supportingText = supportingText.ToNullableTextComposable(), + isError = isError, + keyboardOptions = keyboardOptions.toPlatformValue(), + keyboardActions = keyboardActions.toPlatformValue(), + singleLine = singleLine, + maxLines = lines, + minLines = lines + ) + + +@Composable +actual fun OutlinedTextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + readOnly: Boolean, + label: String?, + placeholder: String?, + leadingIcon: @Composable ((Modifier) -> Unit)?, + trailingIcon: @Composable ((Modifier) -> Unit)?, + prefix: String?, + suffix: String?, + supportingText: String?, + isError: Boolean, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean, + lines: Int +) = + androidx.compose.material3.OutlinedTextField( + value, + onValueChange, + modifier.platformModifier, + enabled, + readOnly, + label = label.ToNullableTextComposable(), + placeholder = placeholder.ToNullableTextComposable(), + leadingIcon = leadingIcon.toContentWithoutModifier(), + trailingIcon = trailingIcon.toContentWithoutModifier(), + prefix = prefix.ToNullableTextComposable(), + suffix = suffix.ToNullableTextComposable(), + supportingText = supportingText.ToNullableTextComposable(), + isError = isError, + keyboardOptions = keyboardOptions.toPlatformValue(), + keyboardActions = keyboardActions.toPlatformValue(), + singleLine = singleLine, + maxLines = lines, + minLines = lines + ) diff --git a/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.androidxCommon.kt b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.androidxCommon.kt new file mode 100644 index 00000000..42c4cb28 --- /dev/null +++ b/compose-multiplatform-material3/src/androidxCommonMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.androidxCommon.kt @@ -0,0 +1,81 @@ +package com.huanshankeji.compose.material3.lazy.ext + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyItemScope +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.material3.ListItem +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.toContentWithoutModifier +import com.huanshankeji.compose.toNullableContentWithoutModifier +import com.huanshankeji.compose.ui.Modifier + +actual class ListScope(val lazyListScope: LazyListScope) { + @Composable + private fun ListItem(content: ListItemComponents) = + with(content) { + ListItem( + headline.toContentWithoutModifier(), + contentModifier.platformModifier, + overline.toNullableContentWithoutModifier(), + supportingText.toNullableContentWithoutModifier(), + start.toNullableContentWithoutModifier(), + trailingSupportingText?.let { trailingSupportingText -> + end?.let { end -> + { + Row { + trailingSupportingText(Modifier) + end(Modifier) + } + } + } ?: { trailingSupportingText(Modifier) } + } ?: end?.let { end -> + { end(Modifier) } + } + ) + } + + + actual fun item( + key: Any?, + contentType: Any?, + content: @Composable ItemScope.() -> Unit + ) = + lazyListScope.item(key, contentType) { ItemScope(this).content() } + + actual fun conventionalItem( + key: Any?, + contentType: Any?, + content: ListItemComponents + ) = + item(key, contentType) { ListItem(content) } + + actual fun items( + count: Int, + key: ((index: Int) -> Any)?, + contentType: (index: Int) -> Any?, + itemContent: @Composable ItemScope.(index: Int) -> Unit + ) = + lazyListScope.items(count, key, contentType) { index -> ItemScope(this).itemContent(index) } + + actual fun conventionalItems( + count: Int, + key: ((index: Int) -> Any)?, + contentType: (index: Int) -> Any?, + itemContent: (index: Int) -> ListItemComponents + ) = + items(count, key, contentType) { index -> + ListItem(itemContent(index)) + } +} + +actual class ItemScope(val lazyItemScope: LazyItemScope) + +@Composable +actual fun List( + modifier: Modifier, + content: ListScope.() -> Unit +) = + LazyColumn(modifier.platformModifier) { + ListScope(this).content() + } diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Button.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Button.kt new file mode 100644 index 00000000..70c4830b --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Button.kt @@ -0,0 +1,65 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.RowScope +import com.huanshankeji.compose.ui.Modifier + +/** + * Filled button + */ +@Composable +expect fun Button( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + /* + shape: Shape = ButtonDefaults.shape, + colors: ButtonColors = ButtonDefaults.buttonColors(), + */ + content: @Composable RowScope.() -> Unit +) + +@Deprecated( + "This is a shortcut to `Button`. Use `Button` instead.", + ReplaceWith("Button(onClick, modifier, enabled, content)", "com.huanshankeji.compose.material3.Button") +) +@Composable +fun FilledButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable RowScope.() -> Unit +) = + Button(onClick, modifier, enabled, content) + +@Composable +expect fun ElevatedButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable RowScope.() -> Unit +) + +@Composable +expect fun FilledTonalButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable RowScope.() -> Unit +) + +@Composable +expect fun OutlinedButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable RowScope.() -> Unit +) + +@Composable +expect fun TextButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable RowScope.() -> Unit +) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Card.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Card.kt new file mode 100644 index 00000000..cfce45ea --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Card.kt @@ -0,0 +1,36 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.ColumnScope +import com.huanshankeji.compose.material3.ext.Card +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun Card( + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit +) + +/** + * An alias for [Card]. + */ +@Composable +fun FilledCard( + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit +) = + Card(modifier, content) + +// TODO clickable cards + +@Composable +expect fun ElevatedCard( + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit +) + +@Composable +expect fun OutlinedCard( + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit +) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Checkbox.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Checkbox.kt new file mode 100644 index 00000000..220f5506 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Checkbox.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun Checkbox( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + enabled: Boolean = true +) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/FloatingActionButton.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/FloatingActionButton.kt new file mode 100644 index 00000000..b1cc89e3 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/FloatingActionButton.kt @@ -0,0 +1,38 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ExtRecommendedApi +import com.huanshankeji.compose.foundation.layout.RowScope +import com.huanshankeji.compose.ui.Modifier + +@ExtRecommendedApi +@Composable +expect fun FloatingActionButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) + +@ExtRecommendedApi +@Composable +expect fun SmallFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) + +@ExtRecommendedApi +@Composable +expect fun LargeFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) + +@ExtRecommendedApi +@Composable +expect fun ExtendedFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable RowScope.() -> Unit +) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Icon.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Icon.kt new file mode 100644 index 00000000..ab926734 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Icon.kt @@ -0,0 +1,13 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun Icon( + icon: Icon, + contentDescription: String?, + modifier: Modifier = Modifier + //tint: Color = LocalContentColor.current +) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/IconButton.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/IconButton.kt new file mode 100644 index 00000000..6ca804d8 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/IconButton.kt @@ -0,0 +1,77 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ExtRecommendedApi +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun IconButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable () -> Unit +) + +@ExtRecommendedApi +@Composable +expect fun IconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable () -> Unit +) + +@Composable +expect fun FilledIconButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable () -> Unit +) + +@ExtRecommendedApi +@Composable +expect fun FilledIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable () -> Unit +) + +@Composable +expect fun FilledTonalIconButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable () -> Unit +) + +@ExtRecommendedApi +@Composable +expect fun FilledTonalIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable () -> Unit +) + +@Composable +expect fun OutlinedIconButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable () -> Unit +) + +@ExtRecommendedApi +@Composable +expect fun OutlinedIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + content: @Composable () -> Unit +) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Switch.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Switch.kt new file mode 100644 index 00000000..0aec1974 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Switch.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun Switch( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier = Modifier, + enabled: Boolean = true +) 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 new file mode 100644 index 00000000..3ae5a7de --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/Text.kt @@ -0,0 +1,16 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material3.ext.InlineText +import com.huanshankeji.compose.ui.Modifier + +/** + * 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.material3.ext.MaterialText + */ +@Composable +expect fun Text(text: String, modifier: Modifier = Modifier) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Button.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Button.kt new file mode 100644 index 00000000..e6f44b71 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Button.kt @@ -0,0 +1,163 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.ui.Modifier + +/** + * filled button + * @param icon the [Modifier] parameter contains the attributes to be set on this icon on JS. You are supposed to pass this [Modifier] to the top-level composable that you invoke inside. + */ +@Composable +expect fun Button( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: @Composable ((Modifier) -> Unit)? = null, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) + +@Composable +fun ButtonWithMaterialIcon( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: Icon?, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) = + Button( + onClick, + modifier, + enabled, + icon.toNullableContentWithModifier(), + isTrailingIcon, + content + ) + +/** + * a shortcut to `Button` + */ +@Composable +fun FilledButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: @Composable ((Modifier) -> Unit)? = null, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) = + Button(onClick, modifier, enabled, icon, isTrailingIcon, content) + +@Composable +expect fun ElevatedButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: @Composable ((Modifier) -> Unit)? = null, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) + +@Composable +fun ElevatedButtonWithMaterialIcon( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: Icon?, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) = + ElevatedButton( + onClick, + modifier, + enabled, + icon.toNullableContentWithModifier(), + isTrailingIcon, + content + ) + +@Composable +expect fun FilledTonalButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: @Composable ((Modifier) -> Unit)? = null, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) + +@Composable +fun FilledTonalButtonWithMaterialIcon( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: Icon?, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) = + FilledTonalButton( + onClick, + modifier, + enabled, + icon.toNullableContentWithModifier(), + isTrailingIcon, + content + ) + +@Composable +expect fun OutlinedButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: @Composable ((Modifier) -> Unit)? = null, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) + +@Composable +fun OutlinedButtonWithMaterialIcon( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: Icon?, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) = + OutlinedButton( + onClick, + modifier, + enabled, + icon.toNullableContentWithModifier(), + isTrailingIcon, + content + ) + +@Composable +expect fun TextButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: @Composable ((Modifier) -> Unit)? = null, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) + +@Composable +fun TextButtonWithMaterialIcon( + onClick: () -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + icon: Icon?, + isTrailingIcon: Boolean = false, + content: @Composable () -> Unit +) = + TextButton( + onClick, + modifier, + enabled, + icon.toNullableContentWithModifier(), + isTrailingIcon, + content + ) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Card.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Card.kt new file mode 100644 index 00000000..66adbd9c --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Card.kt @@ -0,0 +1,32 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun Card( + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) + +/** + * An alias for [Card]. + */ +@Composable +fun FilledCard( + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) = + Card(modifier, content) + +@Composable +expect fun ElevatedCard( + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) + +@Composable +expect fun OutlinedCard( + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/FloatingActionButton.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/FloatingActionButton.kt new file mode 100644 index 00000000..bc0a174e --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/FloatingActionButton.kt @@ -0,0 +1,67 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun FloatingActionButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable (Modifier) -> Unit +) + +@Composable +fun FloatingActionButtonWithMaterialIcon( + onClick: () -> Unit, + modifier: Modifier = Modifier, + icon: Icon +) = + FloatingActionButton(onClick, modifier, icon.toContentWithModifier()) + +@Composable +expect fun SmallFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable (Modifier) -> Unit +) + +@Composable +fun SmallFloatingActionButtonWithMaterialIcon( + onClick: () -> Unit, + modifier: Modifier = Modifier, + icon: Icon +) = + SmallFloatingActionButton(onClick, modifier, icon.toContentWithModifier()) + +@Composable +expect fun LargeFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable (Modifier) -> Unit +) + +@Composable +fun LargeFloatingActionButtonWithMaterialIcon( + onClick: () -> Unit, + modifier: Modifier = Modifier, + icon: Icon +) = + LargeFloatingActionButton(onClick, modifier, icon.toContentWithModifier()) + +@Composable +expect fun ExtendedFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, + label: String, + content: @Composable ((Modifier) -> Unit)? +) + +@Composable +fun ExtendedFloatingActionButtonWithMaterialIcon( + onClick: () -> Unit, + modifier: Modifier = Modifier, + label: String, + icon: Icon? +) = + ExtendedFloatingActionButton(onClick, modifier, label, icon.toNullableContentWithModifier()) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Icon.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Icon.kt new file mode 100644 index 00000000..496ff952 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Icon.kt @@ -0,0 +1,12 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material3.Icon +import com.huanshankeji.compose.ui.Modifier + +fun Icon.toContentWithModifier(): @Composable (Modifier) -> Unit = + { modifier -> Icon(this, null, modifier) } + +fun Icon?.toNullableContentWithModifier(): @Composable ((Modifier) -> Unit)? = + this?.toContentWithModifier() diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/IconButton.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/IconButton.kt new file mode 100644 index 00000000..6ee2d197 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/IconButton.kt @@ -0,0 +1,137 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material3.Icon +import com.huanshankeji.compose.ui.Modifier + +private fun Icon.toUncheckedIconContent(): @Composable () -> Unit = { + Icon(this, null) +} + +private fun Icon.toCheckedIconContent(): @Composable (Modifier) -> Unit = + toContentWithModifier() + +/** + * @param checkedContent the [Modifier] parameter contains the attributes to be set on this icon on JS. You are supposed to pass this [Modifier] to the top-level composable that you invoke inside. + */ +@Composable +expect fun IconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) + +@Composable +fun IconToggleButtonWithMaterialIcons( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + uncheckedIcon: Icon, + checkedIcon: Icon +) = + IconToggleButton( + checked, + onCheckedChange, + modifier, + enabled, + uncheckedIcon.toUncheckedIconContent(), + checkedIcon.toCheckedIconContent() + ) + +/** + * @param checkedContent the [Modifier] parameter contains the attributes to be set on this icon on JS. You are supposed to pass this [Modifier] to the top-level composable that you invoke inside. + */ +@Composable +expect fun FilledIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) + +@Composable +fun FilledIconToggleButtonWithMaterialIcons( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + uncheckedIcon: Icon, + checkedIcon: Icon +) = + FilledIconToggleButton( + checked, + onCheckedChange, + modifier, + enabled, + uncheckedIcon.toUncheckedIconContent(), + checkedIcon.toCheckedIconContent() + ) + +/** + * @param checkedContent the [Modifier] parameter contains the attributes to be set on this icon on JS. You are supposed to pass this [Modifier] to the top-level composable that you invoke inside. + */ +@Composable +expect fun FilledTonalIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) + +@Composable +fun FilledTonalIconToggleButtonWithMaterialIcons( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + uncheckedIcon: Icon, + checkedIcon: Icon +) = + FilledTonalIconToggleButton( + checked, + onCheckedChange, + modifier, + enabled, + uncheckedIcon.toUncheckedIconContent(), + checkedIcon.toCheckedIconContent() + ) + +/** + * @param checkedContent the [Modifier] parameter contains the attributes to be set on this icon on JS. You are supposed to pass this [Modifier] to the top-level composable that you invoke inside. + */ +@Composable +expect fun OutlinedIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) + +@Composable +fun OutlinedIconToggleButtonWithMaterialIcons( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + uncheckedIcon: Icon, + checkedIcon: Icon +) = + OutlinedIconToggleButton( + checked, + onCheckedChange, + modifier, + enabled, + uncheckedIcon.toUncheckedIconContent(), + checkedIcon.toCheckedIconContent() + ) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.kt new file mode 100644 index 00000000..b87420e5 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.kt @@ -0,0 +1,47 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.ui.Modifier + +@Composable +expect fun NavigationBar( + modifier: Modifier = Modifier, + content: @Composable NavigationBarScope.() -> Unit +) + +expect class NavigationBarScope + +@Composable +expect fun NavigationBarScope.NavigationBarItem( + selected: Boolean, + onClick: () -> Unit, + selectedIcon: @Composable (Modifier) -> Unit, + unselectedIcon: @Composable (Modifier) -> Unit = selectedIcon, + modifier: Modifier = Modifier, + enabled: Boolean = true, + label: String? = null, + alwaysShowLabel: Boolean = true, +) + +@Composable +fun NavigationBarScope.NavigationBarItemWithMaterialIcons( + selected: Boolean, + onClick: () -> Unit, + selectedIcon: Icon, + unselectedIcon: Icon = selectedIcon, + modifier: Modifier = Modifier, + enabled: Boolean = true, + label: String? = null, + alwaysShowLabel: Boolean = true, +) = + NavigationBarItem( + selected, + onClick, + selectedIcon.toContentWithModifier(), + unselectedIcon.toContentWithModifier(), + modifier, + enabled, + label, + alwaysShowLabel + ) diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Switch.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Switch.kt new file mode 100644 index 00000000..759f21c5 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Switch.kt @@ -0,0 +1,3 @@ +package com.huanshankeji.compose.material3.ext + +// LabelWithSwitch 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 new file mode 100644 index 00000000..4d52d20a --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/Text.kt @@ -0,0 +1,32 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material3.Text +import com.huanshankeji.compose.ui.Modifier + +/** + * An explicit alias of [Text]. + */ +@Composable +inline fun MaterialText(text: String, modifier: Modifier = Modifier) = + Text(text, modifier) + +/** + * Delegates to raw inline text without any element on JS / Compose HTML. + * @see com.huanshankeji.compose.foundation.text.ext.InlineBasicText + */ +@Composable +expect fun InlineText(text: String) + + +fun String.toTextWithModifier(): @Composable (Modifier) -> Unit = + { modifier -> Text(this, modifier) } + +fun String?.toNullableTextWithModifier(): @Composable ((Modifier) -> Unit)? = + this?.toTextWithModifier() + +fun String.toInlineText(): @Composable () -> Unit = + { InlineText(this) } + +fun String?.toNullableInlineText(): @Composable (() -> Unit)? = + this?.toInlineText() diff --git a/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.kt b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.kt new file mode 100644 index 00000000..9bd90461 --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.kt @@ -0,0 +1,143 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.text.KeyboardActions +import com.huanshankeji.compose.foundation.text.KeyboardOptions +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.ui.Modifier + +/** + * @param leadingIcon the [Modifier] parameter contains the attributes to be set on this icon on JS. You are supposed to pass this [Modifier] to the top-level composable that you invoke inside. + * @param trailingIcon ditto. + */ +@Composable +expect fun TextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + readOnly: Boolean = false, + label: String? = null, + placeholder: String? = null, + leadingIcon: @Composable ((Modifier) -> Unit)? = null, + trailingIcon: @Composable ((Modifier) -> Unit)? = null, + prefix: String? = null, + suffix: String? = null, + supportingText: String? = null, + isError: Boolean = false, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = false, + lines: Int = 1 + /* + maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + minLines: Int = 1 + */ + // pattern: String? // This can be supported with Kotlin's `Regex` so it's not supported here. +) + +@Composable +fun TextFieldWithMaterialIcons( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + readOnly: Boolean = false, + label: String? = null, + placeholder: String? = null, + leadingIcon: Icon? = null, + trailingIcon: Icon? = null, + prefix: String? = null, + suffix: String? = null, + supportingText: String? = null, + isError: Boolean = false, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = false, + lines: Int = 1 +) = + TextField( + value, + onValueChange, + modifier, + enabled, + readOnly, + label, + placeholder, + leadingIcon.toNullableContentWithModifier(), + trailingIcon.toNullableContentWithModifier(), + prefix, + suffix, + supportingText, + isError, + keyboardOptions, + keyboardActions, + singleLine, + lines + ) + + +/** + * @param leadingIcon the [Modifier] parameter contains the attributes to be set on this icon on JS. You are supposed to pass this [Modifier] to the top-level composable that you invoke inside. + * @param trailingIcon ditto. + */ +@Composable +expect fun OutlinedTextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + readOnly: Boolean = false, + label: String? = null, + placeholder: String? = null, + leadingIcon: @Composable ((Modifier) -> Unit)? = null, + trailingIcon: @Composable ((Modifier) -> Unit)? = null, + prefix: String? = null, + suffix: String? = null, + supportingText: String? = null, + isError: Boolean = false, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = false, + lines: Int = 1 +) + +@Composable +fun OutlinedTextFieldWithMaterialIcons( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier, + enabled: Boolean = true, + readOnly: Boolean = false, + label: String? = null, + placeholder: String? = null, + leadingIcon: Icon? = null, + trailingIcon: Icon? = null, + prefix: String? = null, + suffix: String? = null, + supportingText: String? = null, + isError: Boolean = false, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = false, + lines: Int = 1 +) = + OutlinedTextField( + value, + onValueChange, + modifier, + enabled, + readOnly, + label, + placeholder, + leadingIcon.toNullableContentWithModifier(), + trailingIcon.toNullableContentWithModifier(), + prefix, + suffix, + supportingText, + isError, + keyboardOptions, + keyboardActions, + singleLine, + lines + ) 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 new file mode 100644 index 00000000..ef397ade --- /dev/null +++ b/compose-multiplatform-material3/src/commonMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.kt @@ -0,0 +1,70 @@ +package com.huanshankeji.compose.material3.lazy.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.material3.ext.toNullableContentWithModifier +import com.huanshankeji.compose.material3.ext.toNullableTextWithModifier +import com.huanshankeji.compose.material3.ext.toTextWithModifier +import com.huanshankeji.compose.ui.Modifier + +expect class ListScope { + fun item(key: Any? = null, contentType: Any? = null, content: @Composable ItemScope.() -> Unit) + + // the word "conventional" added to be consistent with `conventionalItems` + fun conventionalItem(key: Any? = null, contentType: Any? = null, content: ListItemComponents) + + fun items( + count: Int, + key: ((index: Int) -> Any)? = null, + contentType: (index: Int) -> Any? = { null }, + itemContent: @Composable ItemScope.(index: Int) -> Unit + ) + + // the word "conventional" added to avoid clashing with `items` + fun conventionalItems( + count: Int, + key: ((index: Int) -> Any)? = null, + contentType: (index: Int) -> Any? = { null }, + itemContent: (index: Int) -> ListItemComponents + ) +} + +expect class ItemScope + +class ListItemComponents( + val contentModifier: Modifier = Modifier, + val headline: @Composable (Modifier) -> Unit, + val start: @Composable ((Modifier) -> Unit)? = null, + val end: @Composable ((Modifier) -> Unit)? = null, + val supportingText: @Composable ((Modifier) -> Unit)? = null, + val trailingSupportingText: @Composable ((Modifier) -> Unit)? = null, + val overline: @Composable ((Modifier) -> Unit)? = null +) { + constructor( + contentModifier: Modifier = Modifier, + headline: String, + start: Icon? = null, + end: Icon? = null, + supportingText: String? = null, + trailingSupportingText: String? = null, + overline: String? = null + ) : this( + contentModifier, + headline.toTextWithModifier(), + start.toNullableContentWithModifier(), + end.toNullableContentWithModifier(), + supportingText.toNullableTextWithModifier(), + trailingSupportingText.toNullableTextWithModifier(), + overline.toNullableTextWithModifier() + ) +} + +@Composable +expect fun List(modifier: Modifier = Modifier, content: ListScope.() -> Unit) + +/** + * An alias for [List] that is more similar to `androidx.compose.foundation.layout.LazyColumn`. + */ +@Composable +fun LazyColumnList(modifier: Modifier = Modifier, content: ListScope.() -> Unit) = + List(modifier, content) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Button.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Button.js.kt new file mode 100644 index 00000000..2aa2d171 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Button.js.kt @@ -0,0 +1,135 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.Row +import com.huanshankeji.compose.foundation.layout.RowScope +import com.huanshankeji.compose.html.material3.* +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.web.attributes.isFalseOrNull +import com.huanshankeji.compose.web.attributes.isTrueOrNull + +@Composable +private fun (@Composable (RowScope.() -> Unit)).toMdButtonScopeContent(): @Composable MdButtonScope.() -> Unit = + // TODO consider adding the row styles/classes to the button and remove the wrapping `Row` + { Row(content = this@toMdButtonScopeContent) } + + +@Composable +internal fun CommonButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + isTrailingIcon: Boolean = false, + content: @Composable MdButtonScope.() -> Unit +) = + MdFilledButton( + disabled = enabled.isFalseOrNull(), + trailingIcon = isTrailingIcon.isTrueOrNull(), + attrs = modifier.toCommonButtonAttrs(onClick), + content = content + ) + +@Composable +actual fun Button( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable RowScope.() -> Unit +) = + CommonButton(onClick, modifier, enabled, content = content.toMdButtonScopeContent()) + +@Composable +internal fun CommonElevatedButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + isTrailingIcon: Boolean = false, + content: @Composable MdButtonScope.() -> Unit +) = + MdElevatedButton( + disabled = enabled.isFalseOrNull(), + trailingIcon = isTrailingIcon.isTrueOrNull(), + attrs = modifier.toCommonButtonAttrs(onClick), + content = content + ) + +@Composable +actual fun ElevatedButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable RowScope.() -> Unit +) = + CommonElevatedButton(onClick, modifier, enabled, content = content.toMdButtonScopeContent()) + +@Composable +internal fun CommonFilledTonalButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + isTrailingIcon: Boolean = false, + content: @Composable MdButtonScope.() -> Unit +) = + MdFilledTonalButton( + disabled = enabled.isFalseOrNull(), + trailingIcon = isTrailingIcon.isTrueOrNull(), + attrs = modifier.toCommonButtonAttrs(onClick), + content = content + ) + +@Composable +actual fun FilledTonalButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable RowScope.() -> Unit +) = + CommonFilledTonalButton(onClick, modifier, enabled, content = content.toMdButtonScopeContent()) + +@Composable +internal fun CommonOutlinedButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + isTrailingIcon: Boolean = false, + content: @Composable MdButtonScope.() -> Unit +) = + MdOutlinedButton( + disabled = enabled.isFalseOrNull(), + trailingIcon = isTrailingIcon.isTrueOrNull(), + attrs = modifier.toCommonButtonAttrs(onClick), + content = content + ) + +@Composable +actual fun OutlinedButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable RowScope.() -> Unit +) = + CommonOutlinedButton(onClick, modifier, enabled, content = content.toMdButtonScopeContent()) + +@Composable +internal fun CommonTextButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + isTrailingIcon: Boolean = false, + content: @Composable MdButtonScope.() -> Unit +) = + MdTextButton( + disabled = enabled.isFalseOrNull(), + trailingIcon = isTrailingIcon.isTrueOrNull(), + attrs = modifier.toCommonButtonAttrs(onClick), + content = content + ) + +@Composable +actual fun TextButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable RowScope.() -> Unit +) = + CommonTextButton(onClick, modifier, enabled, content = content.toMdButtonScopeContent()) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Card.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Card.js.kt new file mode 100644 index 00000000..4afc775b --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Card.js.kt @@ -0,0 +1,34 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.ColumnScope +import com.huanshankeji.compose.foundation.layout.toCommonColumnScopeContent +import com.huanshankeji.compose.ui.Modifier +import com.varabyte.kobweb.compose.foundation.layout.Column + +@Composable +actual fun Card( + modifier: Modifier, + content: @Composable ColumnScope.() -> Unit +) = + com.huanshankeji.compose.material3.ext.Card(modifier) { + Column(content = content.toCommonColumnScopeContent()) + } + +@Composable +actual fun ElevatedCard( + modifier: Modifier, + content: @Composable ColumnScope.() -> Unit +) = + com.huanshankeji.compose.material3.ext.ElevatedCard(modifier) { + Column(content = content.toCommonColumnScopeContent()) + } + +@Composable +actual fun OutlinedCard( + modifier: Modifier, + content: @Composable ColumnScope.() -> Unit +) = + com.huanshankeji.compose.material3.ext.OutlinedCard(modifier) { + Column(content = content.toCommonColumnScopeContent()) + } diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Checkbox.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Checkbox.js.kt new file mode 100644 index 00000000..f2bc899c --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Checkbox.js.kt @@ -0,0 +1,30 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.html.material3.MdCheckbox +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs +import com.huanshankeji.compose.web.attributes.ext.onInput +import com.huanshankeji.compose.web.attributes.isFalseOrNull +import com.huanshankeji.compose.web.attributes.isTrueOrNull + +@Composable +actual fun Checkbox( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier, + enabled: Boolean +) = + MdCheckbox( + checked.isTrueOrNull(), + enabled.isFalseOrNull(), + attrs = modifier.toAttrs { + /* + Use `onInput` here because it wraps an `input` element. + Also see: https://stackoverflow.com/questions/58016503/click-vs-input-vs-change-for-checkboxes + */ + + //onCheckedChange?.let { onClick { it(!checked) } } + onCheckedChange?.let { onInput { it(!checked) } } + } + ) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/CommonButton.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/CommonButton.js.kt new file mode 100644 index 00000000..97aa4e1f --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/CommonButton.js.kt @@ -0,0 +1,11 @@ +package com.huanshankeji.compose.material3 + +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs +import com.huanshankeji.compose.web.attributes.Attrs +import org.w3c.dom.HTMLElement + +internal fun Modifier.toCommonButtonAttrs(onClick: () -> Unit): Attrs = + toAttrs { + onClick { onClick() } + } diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/FloatingActionButton.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/FloatingActionButton.js.kt new file mode 100644 index 00000000..fc27c16f --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/FloatingActionButton.js.kt @@ -0,0 +1,66 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.Box +import com.huanshankeji.compose.foundation.layout.Row +import com.huanshankeji.compose.foundation.layout.RowScope +import com.huanshankeji.compose.html.material3.MdFab +import com.huanshankeji.compose.html.material3.MdFabScope +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.PlatformModifier +import com.huanshankeji.compose.ui.toCommonModifier +import com.varabyte.kobweb.compose.ui.attrsModifier + +internal val MdFabScope.slotEqIconModifier + get() = PlatformModifier.attrsModifier { slotEqIcon() }.toCommonModifier() + +private fun (@Composable () -> Unit).toBoxedContentWithModifier(): @Composable (Modifier) -> Unit = + { modifier -> + // The content doesn't show without `slot="icon"` set. + // TODO consider using the Kobweb `Box` after resolving the issue whether `fit-content` is needed on the components + Box(modifier) { + this@toBoxedContentWithModifier() + } + } + + +@Composable +actual fun FloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable () -> Unit +) = + com.huanshankeji.compose.material3.ext.FloatingActionButton( + onClick, modifier, content.toBoxedContentWithModifier() + ) + +@Composable +actual fun SmallFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable () -> Unit +) = + com.huanshankeji.compose.material3.ext.SmallFloatingActionButton( + onClick, modifier, content.toBoxedContentWithModifier() + ) + +@Composable +actual fun LargeFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable () -> Unit +) = + com.huanshankeji.compose.material3.ext.LargeFloatingActionButton( + onClick, modifier, content.toBoxedContentWithModifier() + ) + +@Composable +actual fun ExtendedFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable RowScope.() -> Unit +) = + MdFab(attrs = modifier.toCommonButtonAttrs(onClick)) { + // TODO consider using the Kobweb `Row` after resolving the issue whether `fit-content` is needed on the components + Row(slotEqIconModifier) { content() } + } diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Icon.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Icon.js.kt new file mode 100644 index 00000000..ba60dcef --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Icon.js.kt @@ -0,0 +1,18 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.contentDescription +import com.huanshankeji.compose.html.material3.MdIcon +import com.huanshankeji.compose.material.icons.Icon +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs + +@Composable +actual fun Icon( + icon: Icon, + contentDescription: String?, + modifier: Modifier +) = + MdIcon(attrs = modifier.toAttrs { + contentDescription(contentDescription) + }, materialIconName = icon.name) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/IconButton.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/IconButton.js.kt new file mode 100644 index 00000000..9113e953 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/IconButton.js.kt @@ -0,0 +1,148 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.layout.Box +import com.huanshankeji.compose.html.material3.* +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.PlatformModifier +import com.huanshankeji.compose.ui.toAttrs +import com.huanshankeji.compose.ui.toCommonModifier +import com.huanshankeji.compose.web.attributes.ext.onInput +import com.huanshankeji.compose.web.attributes.isFalseOrNull +import com.huanshankeji.compose.web.attributes.isTrueOrNull +import com.varabyte.kobweb.compose.ui.attrsModifier +import org.jetbrains.compose.web.attributes.AttrsScope +import org.w3c.dom.HTMLElement + +internal fun Modifier.toCommonIconToggleButtonAttrs( + checked: Boolean, onCheckedChange: (Boolean) -> Unit +): AttrsScope.() -> Unit = + toAttrs { + // note that `onInput` is used here + onInput { onCheckedChange(!checked) } + } + +internal fun MdIconButtonScope.slotEqSelectedModifier() = + PlatformModifier.attrsModifier { slotEqSelected() }.toCommonModifier() + +private fun (@Composable () -> Unit).toCommonIconButtonContent(): @Composable MdIconButtonScope.() -> Unit = { + Box { this@toCommonIconButtonContent() } + Box(slotEqSelectedModifier()) { this@toCommonIconButtonContent() } +} + +@Composable +actual fun IconButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + MdIconButton( + enabled.isFalseOrNull(), + attrs = modifier.toCommonButtonAttrs(onClick), + content = content.toCommonIconButtonContent() + ) + + +@Composable +actual fun IconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + MdIconButton( + enabled.isFalseOrNull(), + toggle = true, + selected = checked.isTrueOrNull(), + attrs = modifier.toCommonIconToggleButtonAttrs(checked, onCheckedChange), + content = content.toCommonIconButtonContent() + ) + +@Composable +actual fun FilledIconButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + MdFilledIconButton( + enabled.isFalseOrNull(), + attrs = modifier.toCommonButtonAttrs(onClick), + content = content.toCommonIconButtonContent() + ) + +@Composable +actual fun FilledIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + MdFilledIconButton( + enabled.isFalseOrNull(), + toggle = true, + selected = checked.isTrueOrNull(), + attrs = modifier.toCommonIconToggleButtonAttrs(checked, onCheckedChange), + content = content.toCommonIconButtonContent() + ) + +@Composable +actual fun FilledTonalIconButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + MdFilledTonalIconButton( + enabled.isFalseOrNull(), + attrs = modifier.toCommonButtonAttrs(onClick), + content = content.toCommonIconButtonContent() + ) + +@Composable +actual fun FilledTonalIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + MdFilledTonalIconButton( + enabled.isFalseOrNull(), + toggle = true, + selected = checked.isTrueOrNull(), + attrs = modifier.toCommonIconToggleButtonAttrs(checked, onCheckedChange), + content = content.toCommonIconButtonContent() + ) + +@Composable +actual fun OutlinedIconButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + MdOutlinedIconButton( + enabled.isFalseOrNull(), + attrs = modifier.toCommonButtonAttrs(onClick), + content = content.toCommonIconButtonContent() + ) + +@Composable +actual fun OutlinedIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + content: @Composable () -> Unit +) = + MdOutlinedIconButton( + enabled.isFalseOrNull(), + toggle = true, + selected = checked.isTrueOrNull(), + attrs = modifier.toCommonIconToggleButtonAttrs(checked, onCheckedChange), + content = content.toCommonIconButtonContent() + ) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Switch.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Switch.js.kt new file mode 100644 index 00000000..c682c80b --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Switch.js.kt @@ -0,0 +1,23 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.html.material3.MdSwitch +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs +import com.huanshankeji.compose.web.attributes.ext.onInput +import com.huanshankeji.compose.web.attributes.isFalseOrNull +import com.huanshankeji.compose.web.attributes.isTrueOrNull + +@Composable +actual fun Switch( + checked: Boolean, + onCheckedChange: ((Boolean) -> Unit)?, + modifier: Modifier, + enabled: Boolean +) = + MdSwitch( + enabled.isFalseOrNull(), + checked.isTrueOrNull(), + attrs = modifier.toAttrs { + onCheckedChange?.let { onInput { onCheckedChange(!checked) } } + }) 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 new file mode 100644 index 00000000..4d8e536a --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/Text.js.kt @@ -0,0 +1,9 @@ +package com.huanshankeji.compose.material3 + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.text.BasicText +import com.huanshankeji.compose.ui.Modifier + +@Composable +actual fun Text(text: String, modifier: Modifier) = + BasicText(text, modifier) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Button.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Button.js.kt new file mode 100644 index 00000000..6f186725 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Button.js.kt @@ -0,0 +1,75 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.html.material3.MdButtonScope +import com.huanshankeji.compose.material3.* +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.PlatformModifier +import com.huanshankeji.compose.ui.toCommonModifier +import com.varabyte.kobweb.compose.ui.attrsModifier + +private fun (@Composable () -> Unit).toMdButtonScopeContent( + icon: @Composable ((Modifier) -> Unit)? +): @Composable MdButtonScope.() -> Unit = { + // see https://github.com/material-components/material-web/blob/main/docs/components/button.md#icon + + this@toMdButtonScopeContent() + icon?.invoke(PlatformModifier.attrsModifier { slotEqIcon() }.toCommonModifier()) +} + + +@Composable +actual fun Button( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean, + content: @Composable () -> Unit +) = + CommonButton(onClick, modifier, enabled, isTrailingIcon, content.toMdButtonScopeContent(icon)) + + +@Composable +actual fun ElevatedButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean, + content: @Composable () -> Unit +) = + CommonElevatedButton(onClick, modifier, enabled, isTrailingIcon, content.toMdButtonScopeContent(icon)) + +@Composable +actual fun FilledTonalButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean, + content: @Composable () -> Unit +) = + CommonFilledTonalButton(onClick, modifier, enabled, isTrailingIcon, content.toMdButtonScopeContent(icon)) + +@Composable +actual fun OutlinedButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean, + content: @Composable () -> Unit +) = + CommonOutlinedButton(onClick, modifier, enabled, isTrailingIcon, content.toMdButtonScopeContent(icon)) + +@Composable +actual fun TextButton( + onClick: () -> Unit, + modifier: Modifier, + enabled: Boolean, + icon: @Composable ((Modifier) -> Unit)?, + isTrailingIcon: Boolean, + content: @Composable () -> Unit +) = + CommonTextButton(onClick, modifier, enabled, isTrailingIcon, content.toMdButtonScopeContent(icon)) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Card.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Card.js.kt new file mode 100644 index 00000000..a4d3526e --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Card.js.kt @@ -0,0 +1,24 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.html.material3.MaterialWebLabsApi +import com.huanshankeji.compose.html.material3.MdElevatedCard +import com.huanshankeji.compose.html.material3.MdFilledCard +import com.huanshankeji.compose.html.material3.MdOutlinedCard +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.toAttrs + +@Composable +actual fun Card(modifier: Modifier, content: @Composable () -> Unit) = + @OptIn(MaterialWebLabsApi::class) + MdFilledCard(modifier.toAttrs()) { content() } + +@Composable +actual fun ElevatedCard(modifier: Modifier, content: @Composable () -> Unit) = + @OptIn(MaterialWebLabsApi::class) + MdElevatedCard(modifier.toAttrs()) { content() } + +@Composable +actual fun OutlinedCard(modifier: Modifier, content: @Composable () -> Unit) = + @OptIn(MaterialWebLabsApi::class) + MdOutlinedCard(modifier.toAttrs()) { content() } diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/FloatingActionButton.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/FloatingActionButton.js.kt new file mode 100644 index 00000000..6c3c0292 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/FloatingActionButton.js.kt @@ -0,0 +1,44 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.html.material3.MdFab +import com.huanshankeji.compose.html.material3.MdFabScope +import com.huanshankeji.compose.material3.slotEqIconModifier +import com.huanshankeji.compose.material3.toCommonButtonAttrs +import com.huanshankeji.compose.ui.Modifier + +private fun (@Composable (Modifier) -> Unit).toCommonMdFabContent(): @Composable MdFabScope.() -> Unit = + { this@toCommonMdFabContent(slotEqIconModifier) } + +@Composable +actual fun FloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable (Modifier) -> Unit +) = + MdFab(attrs = modifier.toCommonButtonAttrs(onClick), content = content.toCommonMdFabContent()) + +@Composable +actual fun SmallFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable (Modifier) -> Unit +) = + MdFab(size = "small", attrs = modifier.toCommonButtonAttrs(onClick), content = content.toCommonMdFabContent()) + +@Composable +actual fun LargeFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + content: @Composable (Modifier) -> Unit +) = + MdFab(size = "large", attrs = modifier.toCommonButtonAttrs(onClick), content = content.toCommonMdFabContent()) + +@Composable +actual fun ExtendedFloatingActionButton( + onClick: () -> Unit, + modifier: Modifier, + label: String, + content: @Composable ((Modifier) -> Unit)? +) = + MdFab(label = label, attrs = modifier.toCommonButtonAttrs(onClick), content = content?.toCommonMdFabContent()) diff --git a/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/IconButton.js.kt b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/IconButton.js.kt new file mode 100644 index 00000000..75fefad4 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/IconButton.js.kt @@ -0,0 +1,87 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.html.material3.* +import com.huanshankeji.compose.material3.slotEqSelectedModifier +import com.huanshankeji.compose.material3.toCommonIconToggleButtonAttrs +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.web.attributes.isFalseOrNull +import com.huanshankeji.compose.web.attributes.isTrueOrNull + +private fun commonIconToggleButtonContent( + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +): @Composable MdIconButtonScope.() -> Unit = { + uncheckedContent() + checkedContent(slotEqSelectedModifier()) +} + + +@Composable +actual fun IconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) = + MdIconButton( + enabled.isFalseOrNull(), + toggle = true, + selected = checked.isTrueOrNull(), + attrs = modifier.toCommonIconToggleButtonAttrs(checked, onCheckedChange), + content = commonIconToggleButtonContent(uncheckedContent, checkedContent) + ) + + +@Composable +actual fun FilledIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) = + MdFilledIconButton( + enabled.isFalseOrNull(), + toggle = true, + selected = checked.isTrueOrNull(), + attrs = modifier.toCommonIconToggleButtonAttrs(checked, onCheckedChange), + content = commonIconToggleButtonContent(uncheckedContent, checkedContent) + ) + +@Composable +actual fun FilledTonalIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) = + MdFilledTonalIconButton( + enabled.isFalseOrNull(), + toggle = true, + selected = checked.isTrueOrNull(), + attrs = modifier.toCommonIconToggleButtonAttrs(checked, onCheckedChange), + content = commonIconToggleButtonContent(uncheckedContent, checkedContent) + ) + +@Composable +actual fun OutlinedIconToggleButton( + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + modifier: Modifier, + enabled: Boolean, + uncheckedContent: @Composable () -> Unit, + checkedContent: @Composable (Modifier) -> Unit +) = + MdOutlinedIconButton( + enabled.isFalseOrNull(), + toggle = true, + selected = checked.isTrueOrNull(), + attrs = modifier.toCommonIconToggleButtonAttrs(checked, onCheckedChange), + content = commonIconToggleButtonContent(uncheckedContent, checkedContent) + ) 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 new file mode 100644 index 00000000..957eef75 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/NavigationBar.js.kt @@ -0,0 +1,53 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.html.material3.MaterialWebLabsApi +import com.huanshankeji.compose.html.material3.MdNavigationBar +import com.huanshankeji.compose.html.material3.MdNavigationTab +import com.huanshankeji.compose.html.material3.MdNavigationTabScope +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.PlatformModifier +import com.huanshankeji.compose.ui.toAttrs +import com.huanshankeji.compose.ui.toCommonModifier +import com.huanshankeji.compose.web.attributes.isFalseOrNull +import com.huanshankeji.compose.web.attributes.isTrueOrNull +import com.varabyte.kobweb.compose.ui.attrsModifier +import org.jetbrains.compose.web.dom.ElementScope +import org.w3c.dom.HTMLElement + +@Composable +actual fun NavigationBar( + modifier: Modifier, + content: @Composable NavigationBarScope.() -> Unit +) = + @OptIn(MaterialWebLabsApi::class) + MdNavigationBar(attrs = modifier.toAttrs()) { + NavigationBarScope(this).content() + } + +actual class NavigationBarScope(val elementScope: ElementScope) + +@Composable +actual fun NavigationBarScope.NavigationBarItem( + selected: Boolean, + onClick: () -> Unit, + selectedIcon: @Composable (Modifier) -> Unit, + unselectedIcon: @Composable (Modifier) -> Unit, + modifier: Modifier, + enabled: Boolean, + label: String?, + alwaysShowLabel: Boolean +) = + @OptIn(MaterialWebLabsApi::class) + MdNavigationTab( + enabled.isFalseOrNull(), + selected.isTrueOrNull(), + alwaysShowLabel.isFalseOrNull(), + label, + attrs = modifier.toAttrs() + ) { + selectedIcon(PlatformModifier.attrsModifier { slot(MdNavigationTabScope.Slot.ActiveIcon) }.toCommonModifier()) + unselectedIcon( + PlatformModifier.attrsModifier { slot(MdNavigationTabScope.Slot.InactiveIcon) }.toCommonModifier() + ) + } 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 new file mode 100644 index 00000000..d71b78f9 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/Text.js.kt @@ -0,0 +1,8 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import org.jetbrains.compose.web.dom.Text + +@Composable +actual fun InlineText(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 new file mode 100644 index 00000000..971f815a --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/ext/TextField.js.kt @@ -0,0 +1,127 @@ +package com.huanshankeji.compose.material3.ext + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +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.html.material3.MdFilledTextField +import com.huanshankeji.compose.html.material3.MdOutlinedTextField +import com.huanshankeji.compose.html.material3.MdTextFieldScope +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.PlatformModifier +import com.huanshankeji.compose.ui.toAttrs +import com.huanshankeji.compose.ui.toCommonModifier +import com.huanshankeji.compose.web.attributes.ext.onInput +import com.huanshankeji.compose.web.attributes.isFalseOrNull +import com.huanshankeji.compose.web.attributes.isTrueOrNull +import com.huanshankeji.compose.web.dom.ext.value +import com.varabyte.kobweb.compose.ui.attrsModifier +import org.jetbrains.compose.web.attributes.AttrsScope +import org.w3c.dom.HTMLElement + +private fun Modifier.toTextFieldAttrs( + onValueChange: (String) -> Unit, keyboardOptions: KeyboardOptions, keyboardActions: KeyboardActions, +): AttrsScope.() -> Unit = + toAttrs { + // see https://stackoverflow.com/questions/574941/best-way-to-track-onchange-as-you-type-in-input-type-text + onInput { onValueChange(it.target.value) } + + // TODO `keyboardOptions.imeAction` is not working because `enterkeyhint` is not passed to the underlying `input`. + attrsFrom(keyboardOptions, keyboardActions) + } + +private fun TextFieldContent( + value: String, + leadingIcon: @Composable ((Modifier) -> Unit)?, + trailingIcon: @Composable ((Modifier) -> Unit)?, +): @Composable MdTextFieldScope.() -> Unit = { + with(elementScope) { + DisposableEffect(value) { + scopeElement.value = value + onDispose {} + } + } + leadingIcon?.invoke(PlatformModifier.attrsModifier { slot(MdTextFieldScope.Slot.LeadingIcon) }.toCommonModifier()) + trailingIcon?.invoke(PlatformModifier.attrsModifier { slot(MdTextFieldScope.Slot.TrailingIcon) }.toCommonModifier()) +} + + +@Composable +actual fun TextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + readOnly: Boolean, + label: String?, + placeholder: String?, + leadingIcon: @Composable ((Modifier) -> Unit)?, + trailingIcon: @Composable ((Modifier) -> Unit)?, + prefix: String?, + suffix: String?, + supportingText: String?, + isError: Boolean, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean, + lines: Int +) = + MdFilledTextField( + enabled.isFalseOrNull(), + isError.isTrueOrNull(), + supportingText, // TODO Is passing `supportingText` as `errorText` correct? + label, + value = value, + prefixText = prefix, + suffixText = suffix, + hasLeadingIcon = leadingIcon?.let { true }, + hasTrailingIcon = trailingIcon?.let { true }, + supportingText = supportingText, + rows = if (singleLine) null else lines, + placeholder = placeholder, + readOnly = readOnly.isTrueOrNull(), + + attrs = modifier.toTextFieldAttrs(onValueChange, keyboardOptions, keyboardActions), + content = TextFieldContent(value, leadingIcon, trailingIcon) + ) + + +@Composable +actual fun OutlinedTextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean, + readOnly: Boolean, + label: String?, + placeholder: String?, + leadingIcon: @Composable ((Modifier) -> Unit)?, + trailingIcon: @Composable ((Modifier) -> Unit)?, + prefix: String?, + suffix: String?, + supportingText: String?, + isError: Boolean, + keyboardOptions: KeyboardOptions, + keyboardActions: KeyboardActions, + singleLine: Boolean, + lines: Int +) = + MdOutlinedTextField( + enabled.isFalseOrNull(), + isError.isTrueOrNull(), + supportingText, // TODO Is passing `supportingText` as `errorText` correct? + label, + value = value, + prefixText = prefix, + suffixText = suffix, + hasLeadingIcon = leadingIcon?.let { true }, + hasTrailingIcon = trailingIcon?.let { true }, + supportingText = supportingText, + rows = if (singleLine) null else lines, + placeholder = placeholder, + readOnly = readOnly.isTrueOrNull(), + + 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 new file mode 100644 index 00000000..e39b0b81 --- /dev/null +++ b/compose-multiplatform-material3/src/jsMain/kotlin/com/huanshankeji/compose/material3/lazy/ext/List.js.kt @@ -0,0 +1,96 @@ +package com.huanshankeji.compose.material3.lazy.ext + +import androidx.compose.runtime.Composable +import com.huanshankeji.compose.foundation.verticalScrollPlatformModifier +import com.huanshankeji.compose.html.material3.MdList +import com.huanshankeji.compose.html.material3.MdListItemScope +import com.huanshankeji.compose.html.material3.MdListScope +import com.huanshankeji.compose.runtime.DeferredComposableRunner +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.ui.PlatformModifier +import com.huanshankeji.compose.ui.toAttrs +import com.huanshankeji.compose.ui.toCommonModifier +import com.varabyte.kobweb.compose.ui.attrsModifier +import com.varabyte.kobweb.compose.ui.toAttrs + +@Composable +private fun MdListItemScope.contentFromComponents(listItemComponents: ListItemComponents) = with(listItemComponents) { + headline.invoke(PlatformModifier.attrsModifier { slot(MdListItemScope.Slot.Headline) }.toCommonModifier()) + start?.invoke(PlatformModifier.attrsModifier { slot(MdListItemScope.Slot.Start) }.toCommonModifier()) + end?.invoke(PlatformModifier.attrsModifier { slot(MdListItemScope.Slot.End) }.toCommonModifier()) + supportingText?.invoke( + PlatformModifier.attrsModifier { slot(MdListItemScope.Slot.SupportingText) }.toCommonModifier() + ) + trailingSupportingText?.invoke( + PlatformModifier.attrsModifier { slot(MdListItemScope.Slot.TrailingSupportingText) }.toCommonModifier() + ) + overline?.invoke(PlatformModifier.attrsModifier { slot(MdListItemScope.Slot.Overline) }.toCommonModifier()) +} + +actual class ListScope(val mdListScope: MdListScope) { + private val deferredComposableRunner = DeferredComposableRunner() + + private fun addComposable(composable: @Composable () -> Unit) = + deferredComposableRunner.addComposable(composable) + + @Composable + internal fun ComposableRun(content: ListScope.() -> Unit) = + deferredComposableRunner.ComposableRun { content() } + + + @Composable + private fun ListItem(content: ListItemComponents) = + mdListScope.MdListItem(attrs = content.contentModifier.toAttrs()) { + contentFromComponents(content) + } + + + actual fun item( + key: Any?, + contentType: Any?, + content: @Composable ItemScope.() -> Unit + ) = addComposable { + mdListScope.MdListItem { ItemScope(this).content() } + } + + actual fun conventionalItem( + key: Any?, + contentType: Any?, + content: ListItemComponents + ) = addComposable { + ListItem(content) + } + + actual fun items( + count: Int, + key: ((index: Int) -> Any)?, + contentType: (index: Int) -> Any?, + itemContent: @Composable ItemScope.(index: Int) -> Unit + ) = addComposable { + repeat(count) { index -> + mdListScope.MdListItem { ItemScope(this).itemContent(index) } + } + } + + actual fun conventionalItems( + count: Int, + key: ((index: Int) -> Any)?, + contentType: (index: Int) -> Any?, + itemContent: (index: Int) -> ListItemComponents + ) = addComposable { + repeat(count) { index -> + ListItem(itemContent(index)) + } + } +} + +actual class ItemScope(val mdListItemScope: MdListItemScope) + +@Composable +actual fun List( + modifier: Modifier, + content: ListScope.() -> Unit +) = + MdList(verticalScrollPlatformModifier.then(modifier.platformModifier).toAttrs()) { + ListScope(this).ComposableRun(content) + } diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts index 04bf8fc3..00f7929a 100644 --- a/demo/build.gradle.kts +++ b/demo/build.gradle.kts @@ -46,7 +46,8 @@ kotlin { commonMain { dependencies { implementation(compose.runtime) - implementation(project(":compose-multiplatform-material")) + implementation(project(":compose-multiplatform-material2")) + implementation(project(":compose-multiplatform-material3")) } } jvmMain { @@ -76,6 +77,7 @@ kotlin { jsMain { dependencies { implementation(compose.html.core) + implementation(npm("material-symbols", DependencyVersions.materialSymbols)) } } } 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 127c08fe..90513c41 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 @@ -1,124 +1,35 @@ package com.huanshankeji.compose.material.demo -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp -import com.huanshankeji.compose.foundation.background -import com.huanshankeji.compose.foundation.border -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.RowScope -import com.huanshankeji.compose.foundation.text.BasicText -import com.huanshankeji.compose.layout.height +import com.huanshankeji.compose.foundation.rememberScrollState +import com.huanshankeji.compose.foundation.verticalScroll import com.huanshankeji.compose.layout.padding -import com.huanshankeji.compose.layout.size -import com.huanshankeji.compose.layout.width -import com.huanshankeji.compose.material.* -import com.huanshankeji.compose.material.ext.IconButton -import com.huanshankeji.compose.material.ext.TopAppBarScaffold -import com.huanshankeji.compose.material.icons.Icons -import com.huanshankeji.compose.material.icons.filled.Add -import com.huanshankeji.compose.material.icons.filled.Menu -import com.huanshankeji.compose.material.icons.filled.Search -import com.huanshankeji.compose.material.lazy.ext.LazyColumn import com.huanshankeji.compose.ui.Modifier -import com.huanshankeji.compose.ui.graphics.Color -import com.huanshankeji.compose.material.ext.Button as ExtButton -@Composable -fun App() { - TopAppBarScaffold({ - Text("Compose Multiplatform Material demo") - }, navigationIcon = { - MaterialIconNavButton({}, Icons.Default.Menu, "menu") - }, actions = { - MaterialIconActionButton({}, Icons.Default.Search, "search") - }) { - /* - // TODO use this - Modifier.padding(16.dp).height(800.dp).width(400.dp) - */ - Card(Modifier.padding(16.dp).height(800.dp).width(400.dp)) { - Column(Modifier.padding(16.dp)) { - BasicText("basic text 1") - BasicText("basic text 2") - Text("Material text") - - var count by remember { mutableStateOf(0) } - val onClick: () -> Unit = { count++ } - - Row { - val buttonContent: @Composable RowScope.() -> Unit = { - Text(count.toString()) - } - Button(onClick, content = buttonContent) - OutlinedButton(onClick, content = buttonContent) - TextButton(onClick, content = buttonContent) - ExtButton(onClick) { - Label(count.toString()) - } - IconButton(onClick, icon = Icons.Default.Add, contentDescription = "increment count") - } - - Box(Modifier.padding(16.dp)) { - LazyColumn(Modifier.height(100.dp)) { - item { - Text("Ungrouped item") - } - items(count) { - Text("Ungrouped item $it/$count") - } - group(headerContent = { - Text("Group title") - }) { - item { - Text("Grouped item") - } - items(count) { - Text("Grouped item $it/$count") - } - } - } - } - - @Composable - fun ColorBox(color: Color) = - Box(Modifier.padding(8.dp).background(color).size(40.dp)) - - val halfGreen = Color(0, 0x80, 0x00) - - Row(Modifier.roundedCornerBackgroundAndOuterBorder(4.dp, Color.Blue, 16.dp, halfGreen)) { - ColorBox(Color.Red) - ColorBox(Color(0xFF, 0, 0)) - ColorBox(Color(0xFF, 0, 0, 0x80)) - ColorBox(Color(1f, 0f, 0f, 0.5f)) - } +internal enum class RadioButtonState { + A, B, C +} - Row { - @Composable - fun NestedColorBox(modifier: Modifier) = - Box(modifier.background(halfGreen)) { ColorBox(Color.Red) } +val listSize = 160.dp - NestedColorBox(Modifier.border(4.dp, Color.Blue)) - NestedColorBox(Modifier.outerBorder(4.dp, Color.Blue)) - NestedColorBox(Modifier.roundedCornerOuterBorder(4.dp, Color.Blue, 16.dp)) - NestedColorBox(Modifier.roundedCornerOuterBorder(1.dp, Color.Blue, 16.dp)) - val transparentBlue = Color(0, 0, 0x80, 0x80) - Box(Modifier.roundedCornerBackgroundAndOuterBorder(2.dp, transparentBlue, 16.dp, halfGreen)) { - ColorBox(Color.Red) - } - } +fun Modifier.contentPadding() = padding(16.dp) +val contentPaddingModifier = Modifier.contentPadding() - var text by remember { mutableStateOf("") } - TextField( - text, { text = it }, - label = "Demo text field", - leadingIcon = { Icon(Icons.Default.Add, null) }, - trailingIcon = { Icon(Icons.Default.Menu, null) }) - } +@Composable +fun App() { + Row(/*Modifier.height(720.dp)*/) { + @Composable + fun subDemoModifier() = + Modifier.weight(1f).verticalScroll(rememberScrollState()) + + 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())) } + 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 new file mode 100644 index 00000000..54120689 --- /dev/null +++ b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Common.kt @@ -0,0 +1,82 @@ +package com.huanshankeji.compose.material.demo + +import androidx.compose.runtime.* +import androidx.compose.ui.unit.dp +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.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.ui.Modifier +import com.huanshankeji.compose.ui.graphics.Color + +@Composable +fun Common(modifier: Modifier) { + Column(modifier) { + BasicText("basic text 1") + BasicText("basic text 2") + + @Composable + fun ColorBox(color: Color) = + Box(Modifier.padding(8.dp).background(color).size(40.dp)) + + val halfGreen = Color(0, 0x80, 0x00) + + Row(Modifier.roundedCornerBackgroundAndOuterBorder(4.dp, Color.Blue, 16.dp, halfGreen)) { + ColorBox(Color.Red) + ColorBox(Color(0xFF, 0, 0)) + ColorBox(Color(0xFF, 0, 0, 0x80)) + ColorBox(Color(1f, 0f, 0f, 0.5f)) + } + + Row { + @Composable + fun NestedColorBox(modifier: Modifier) = + Box(modifier.background(halfGreen)) { ColorBox(Color.Red) } + + NestedColorBox(Modifier.border(4.dp, Color.Blue)) + NestedColorBox(Modifier.outerBorder(4.dp, Color.Blue)) + NestedColorBox(Modifier.roundedCornerOuterBorder(4.dp, Color.Blue, 16.dp)) + NestedColorBox(Modifier.roundedCornerOuterBorder(1.dp, Color.Blue, 16.dp)) + val transparentBlue = Color(0, 0, 0x80, 0x80) + Box(Modifier.roundedCornerBackgroundAndOuterBorder(2.dp, transparentBlue, 16.dp, halfGreen)) { + ColorBox(Color.Red) + } + } + + var count by remember { mutableStateOf(0) } + BasicText("Click to add items", Modifier.onClick { count++ }) + val lazyListContent: LazyListScope.() -> Unit = { + item { BasicText("Item") } + items(count) { index -> BasicText("Item $index") } + } + BasicText("`LazyColumn`") + LazyColumn(Modifier.height(listSize), content = lazyListContent) + BasicText("`LazyRow`") + LazyRow(Modifier.width(listSize), content = lazyListContent) + + @Composable + fun ColumnOrRowContent() { + BasicText("Item") + repeat(count) { index -> BasicText("Item $index") } + } + BasicText("`Column` with scroll") + Column(Modifier.height(listSize).verticalScroll(rememberScrollState())) { + ColumnOrRowContent() + } + BasicText("`Row` with scroll") + Row(Modifier.width(listSize).horizontalScroll(rememberScrollState())) { + ColumnOrRowContent() + } + } +} \ 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 new file mode 100644 index 00000000..eb7186c7 --- /dev/null +++ b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material2.kt @@ -0,0 +1,138 @@ +package com.huanshankeji.compose.material.demo + +import androidx.compose.runtime.* +import androidx.compose.ui.unit.dp +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.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.Menu +import com.huanshankeji.compose.material.icons.filled.Search +import com.huanshankeji.compose.material2.* +import com.huanshankeji.compose.material2.ext.* +import com.huanshankeji.compose.material2.lazy.ext.List +import com.huanshankeji.compose.material2.lazy.ext.ListItemComponents +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 com.huanshankeji.compose.material2.ext.Button as ExtButton + +@Composable +fun Material2(modifier: Modifier) { + // 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") + }, actions = { + MaterialIconActionButton({}, Icons.Default.Search, "search") + }) { + Card(modifier.contentPadding()) { + Column(contentPaddingModifier.background(Color(0xF8, 0xF8, 0xF8, 0xFF))) { + 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()) + } + IconButton(onClick, icon = Icons.Default.Add, contentDescription = "increment count") + } + + val listModifier = Modifier.padding(16.dp).height(listSize) + List(listModifier) { + item { + Text("Ungrouped item") + } + items(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)) + 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 selected by remember { mutableStateOf(RadioButtonState.A) } + RadioGroupRow { + @Composable + fun RadioButtonRow(state: RadioButtonState) = + RadioRow(selected == state, state.toString(), { selected = state }) + RadioButtonState.entries.forEach { RadioButtonRow(it) } + } + + 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 new file mode 100644 index 00000000..8d97f876 --- /dev/null +++ b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt @@ -0,0 +1,184 @@ +package com.huanshankeji.compose.material.demo + +import androidx.compose.runtime.* +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.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.material.icons.Icons +import com.huanshankeji.compose.material.icons.filled.Add +import com.huanshankeji.compose.material.icons.filled.Menu +import com.huanshankeji.compose.material.icons.filled.Remove +import com.huanshankeji.compose.material3.* +import com.huanshankeji.compose.material3.ext.* +import com.huanshankeji.compose.material3.ext.Card +import com.huanshankeji.compose.material3.ext.ElevatedCard +import com.huanshankeji.compose.material3.ext.OutlinedCard +import com.huanshankeji.compose.material3.lazy.ext.List +import com.huanshankeji.compose.material3.lazy.ext.ListItemComponents +import com.huanshankeji.compose.ui.Modifier +import com.huanshankeji.compose.material3.Button as RowScopeButton + +@Composable +fun Material3(modifier: Modifier) { + Column(modifier) { + var count by remember { mutableStateOf(0) } + val onClick: () -> Unit = { count++ } + val buttonContent: @Composable () -> Unit = { + InlineText(count.toString()) + } + val rowScopeButtonContent: @Composable RowScope.() -> Unit = { buttonContent() } + Row { + RowScopeButton(onClick, content = rowScopeButtonContent) + Button(onClick, content = buttonContent) + ElevatedButton(onClick, content = buttonContent) + FilledTonalButton(onClick, content = buttonContent) + OutlinedButton(onClick, content = buttonContent) + TextButton(onClick, content = buttonContent) + } + Row { + ButtonWithMaterialIcon(onClick, icon = Icons.Default.Add, content = buttonContent) + ButtonWithMaterialIcon(onClick, icon = Icons.Default.Add, isTrailingIcon = true, content = buttonContent) + } + val iconButtonContent: @Composable () -> Unit = { + Icon(Icons.Default.Add, null) + } + Row { + IconButton(onClick, content = iconButtonContent) + FilledIconButton(onClick, content = iconButtonContent) + FilledTonalIconButton(onClick, content = iconButtonContent) + OutlinedIconButton(onClick, content = iconButtonContent) + } + val (checked, onCheckedChange) = remember { mutableStateOf(false) } + val iconToggleButtonContent: @Composable () -> Unit = { + Icon(if (checked) Icons.Default.Add else Icons.Default.Remove, null) + } + @OptIn(ExtRecommendedApi::class) + Row { + IconToggleButton(checked, onCheckedChange, content = iconToggleButtonContent) + FilledIconToggleButton(checked, onCheckedChange, content = iconToggleButtonContent) + FilledTonalIconToggleButton(checked, onCheckedChange, content = iconToggleButtonContent) + OutlinedIconToggleButton(checked, onCheckedChange, content = iconToggleButtonContent) + } + Row { + IconToggleButtonWithMaterialIcons( + checked, onCheckedChange, + uncheckedIcon = Icons.Default.Remove, checkedIcon = Icons.Default.Add + ) + FilledIconToggleButtonWithMaterialIcons( + checked, onCheckedChange, + uncheckedIcon = Icons.Default.Remove, checkedIcon = Icons.Default.Add + ) + FilledTonalIconToggleButtonWithMaterialIcons( + checked, onCheckedChange, + uncheckedIcon = Icons.Default.Remove, checkedIcon = Icons.Default.Add + ) + OutlinedIconToggleButtonWithMaterialIcons( + checked, onCheckedChange, + uncheckedIcon = Icons.Default.Remove, checkedIcon = Icons.Default.Add + ) + } + @OptIn(ExtRecommendedApi::class) + Row { + FloatingActionButton(onClick, content = iconButtonContent) + SmallFloatingActionButton(onClick, content = iconButtonContent) + LargeFloatingActionButton(onClick, content = iconButtonContent) + ExtendedFloatingActionButton(onClick) { + iconButtonContent() + Text("Add") + } + } + Row { + FloatingActionButtonWithMaterialIcon(onClick, icon = Icons.Default.Add) + SmallFloatingActionButtonWithMaterialIcon(onClick, icon = Icons.Default.Add) + LargeFloatingActionButtonWithMaterialIcon(onClick, icon = Icons.Default.Add) + ExtendedFloatingActionButtonWithMaterialIcon(onClick, label = "Add", icon = Icons.Default.Add) + } + Row { + Checkbox(checked, onCheckedChange) + Switch(checked, onCheckedChange) + } + + var text by remember { mutableStateOf("") } + val label = "label" + val placeholder = "placeholder" + val prefix = "prefix" + val suffix = "suffix" + val supportingText = "supporting text" + TextFieldWithMaterialIcons( + text, + { text = it }, + label = label, + placeholder = placeholder, + leadingIcon = Icons.Default.Add, + trailingIcon = Icons.Default.Menu, + prefix = prefix, + suffix = suffix, + supportingText = supportingText, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number) + ) + OutlinedTextFieldWithMaterialIcons( + text, + { text = it }, + label = label, + placeholder = placeholder, + leadingIcon = Icons.Default.Add, + trailingIcon = Icons.Default.Menu, + prefix = prefix, + suffix = suffix, + supportingText = supportingText, + keyboardOptions = KeyboardOptions( + KeyboardCapitalization.Words, true, imeAction = ImeAction.Search + ), + keyboardActions = KeyboardActions { + println("keyboard actions with: $text") + } + ) + + Text("Click a button to show the list:") + List(Modifier.height(listSize)) { + fun content(index: String) = + ListItemComponents( + Modifier, + "Headline $index", + Icons.Default.Add, + Icons.Default.Menu, + "Supporting text $index", + "Trailing supporting text $index", + "Overline $index" + ) + conventionalItem(content = content("")) + conventionalItems(count) { index -> + content(index.toString()) + } + } + + Row { + Card { Text("card", contentPaddingModifier) } + ElevatedCard { Text("elevated card", contentPaddingModifier) } + OutlinedCard { Text("outlined card", contentPaddingModifier) } + } + + var selectedIndex by remember { mutableStateOf(0) } + NavigationBar { + NavigationBarItemWithMaterialIcons( + selectedIndex == 0, + { selectedIndex = 0 }, + Icons.Default.Add, + label = "Add" + ) + NavigationBarItemWithMaterialIcons( + selectedIndex == 1, + { selectedIndex = 1 }, + Icons.Default.Remove, + label = "Remove" + ) + } + } +} diff --git a/demo/src/jsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt b/demo/src/jsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt index a80eff37..8766b2c7 100644 --- a/demo/src/jsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt +++ b/demo/src/jsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt @@ -1,7 +1,9 @@ package com.huanshankeji.compose.material.demo +import com.huanshankeji.compose.html.material3.require import org.jetbrains.compose.web.renderComposableInBody fun main() { + require("material-symbols/outlined.css") renderComposableInBody { App() } } diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index b57acf80..56c58375 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -57,11 +57,28 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@js-joda/core@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" + integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== +"@lit-labs/ssr-dom-shim@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz#353ce4a76c83fadec272ea5674ede767650762fd" + integrity sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g== + +"@lit/reactive-element@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@lit/reactive-element/-/reactive-element-2.0.4.tgz#8f2ed950a848016383894a26180ff06c56ae001b" + integrity sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.2.0" + "@material/animation@^14.0.0": version "14.0.0" resolved "https://registry.yarnpkg.com/@material/animation/-/animation-14.0.0.tgz#f23fbe38deb6a48829dcdb0b7580017a4217e94b" @@ -738,6 +755,14 @@ "@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== + dependencies: + lit "^2.7.4 || ^3.0.0" + tslib "^2.4.0" + "@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" @@ -894,6 +919,11 @@ dependencies: "@types/node" "*" +"@types/trusted-types@^2.0.2": + version "2.0.7" + 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" @@ -2308,6 +2338,31 @@ launch-editor@^2.6.0: picocolors "^1.0.0" shell-quote "^1.8.1" +lit-element@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-4.0.4.tgz#e0b37ebbe2394bcb9578d611a409f49475dff361" + integrity sha512-98CvgulX6eCPs6TyAIQoJZBCQPo80rgXR+dVBs61cstJXqtI+USQZAbA4gFHh6L/mxBx9MrgPLHLsUgDUHAcCQ== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.2.0" + "@lit/reactive-element" "^2.0.4" + lit-html "^3.1.2" + +lit-html@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-3.1.2.tgz#6655ce82367472de7680c62b1bcb0beb0e426fa1" + integrity sha512-3OBZSUrPnAHoKJ9AMjRL/m01YJxQMf+TMHanNtTHG68ubjnZxK0RFl102DPzsw4mWnHibfZIBJm3LWCZ/LmMvg== + dependencies: + "@types/trusted-types" "^2.0.2" + +"lit@^2.7.4 || ^3.0.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/lit/-/lit-3.1.2.tgz#f276258e8a56593f1d834a5fd00a7eb5e891ae73" + integrity sha512-VZx5iAyMtX7CV4K8iTLdCkMaYZ7ipjJZ0JcSdJ0zIdGxxyurjIn7yuuSxNBD7QmjvcNJwr0JS4cAdAtsy7gZ6w== + dependencies: + "@lit/reactive-element" "^2.0.4" + lit-element "^4.0.4" + lit-html "^3.1.2" + loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -2363,6 +2418,11 @@ material-icons@^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== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -3290,6 +3350,11 @@ tslib@^2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +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== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" diff --git a/settings.gradle.kts b/settings.gradle.kts index 1c913d14..128fa6fe 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,7 +3,9 @@ rootProject.name = "compose-multiplatform-material" include("compose-multiplatform-common") // TODO consider splitting into several modules including `foundation`, `ui`, etc. include("compose-multiplatform-common:legacy") project(":compose-multiplatform-common:legacy").name = "compose-multiplatform-common-legacy" -include("compose-multiplatform-material") +include("compose-multiplatform-material-icons-core") +include("compose-multiplatform-material2") +include("compose-multiplatform-material3") include("demo")