Skip to content

Commit

Permalink
Merge branch 'dependent-on-compose-web-material-snapshot'
Browse files Browse the repository at this point in the history
  • Loading branch information
ShreckYe committed Jan 10, 2023
2 parents 7470ab6 + d8c7ed2 commit 8f04066
Show file tree
Hide file tree
Showing 62 changed files with 1,075 additions and 133 deletions.
47 changes: 42 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,53 @@
# Compose Multiplatform Material wrappers

Some simple Compose Multiplatform Material Design component wrappers for desktop, Android, and web (mainly based on [KMDC](https://github.com/mpetuska/kmdc))
[![Maven Central](https://img.shields.io/maven-central/v/com.huanshankeji/compose-multiplatform-material)](https://search.maven.org/artifact/com.huanshankeji/compose-multiplatform-material)

We try to make the styles of the composable components follow those of the desktop and Android ones in `com.huanshankeji.compose.material`, meanwhile being compatible with the Web APIs. However, only a subset of the composable arguments is supported due to the API differences and limitations of the web composables this project depends on.
Some simple Compose Multiplatform wrappers of common components, layouts, and Material Design components for desktop,
Android, and web (mainly based on [KMDC](https://github.com/mpetuska/kmdc))

Customizing styles (using `Modifier`s for desktop and Android, and `attrs: AttrBuilderContext<*>?` for web) is not supported yet.
We try to make the function types of the composable components follow those of the desktop and Android ones
in `androidx.compose.foundation` and `androidx.compose.material`, meanwhile being compatible with the Web APIs. However,
only subsets of the composables and composable arguments are supported due to the API differences, limitations of the
web composables this project depends on, and our limited effort.

Visual consistency across different platforms is not guaranteed.

There is no documentation for this project yet. Check out the demo project on how to use the components.
This project is prototype and there is no documentation yet. Check out [the demo project](demo) on how to use the components.

## Supported components
<!--
There is no plan to support Apple platforms until there is official support from [Compose Multiplatform](https://github.com/JetBrains/compose-jb). Check out <https://github.com/cl3m/multiplatform-compose> for some experiments and prototypes on supporting iOS with Compose Multiplatform.
-->

## Supported features

### Components

#### Common (Foundation) components

- `BasicText`
- `RawText`

##### Layouts

- `Box`
- `Column` (via flexbox on web)
- `Row` (via flexbox on web)

#### Material components

- `Button`
- `Card`
- `Icon`
- `IconButton`
- `ScrollableList`/`LazyColumn` (visually inconsistent for now)
- `Text`/`MaterialText`
- `TopAppBarScaffold`

### styles

- `height`
- `margin`
- `width`
- `backgroundColor`
- `border`
- `outerBorder`
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repositories {

dependencies {
implementation(kotlin("gradle-plugin", "1.7.20"))
implementation("org.jetbrains.compose:compose-gradle-plugin:1.2.1")
implementation("org.jetbrains.compose:compose-gradle-plugin:1.2.2")
implementation("com.huanshankeji.team:gradle-plugins:0.3.2") {
exclude("org.jetbrains.kotlin")
}
Expand Down
3 changes: 3 additions & 0 deletions buildSrc/src/main/kotlin/VersionsAndDependencies.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object DependencyVersions {
val huanshankejiComposeWeb = "0.2.0"
}
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/common-conventions.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
}

repositories {
mavenLocal()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
google()
Expand Down
3 changes: 3 additions & 0 deletions compose-multiplatform-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ kotlin {
named("commonMain") {
dependencies {
implementation(compose.runtime)
//compileOnly(compose.foundation) // for KDoc element links only
}
}
named("jvmMain") {
Expand All @@ -17,6 +18,8 @@ kotlin {
named("jsMain") {
dependencies {
implementation(compose.web.core)

api("com.huanshankeji:compose-web-common:${DependencyVersions.huanshankejiComposeWeb}")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.huanshankeji.compose

import androidx.compose.runtime.Composable
import com.huanshankeji.compose.ui.Element
import com.huanshankeji.compose.ui.ModifierOrAttrs

expect abstract class BasicTextElement : Element

@Composable
expect fun BasicText(text: String)
expect fun BasicText(text: String, modifierOrAttrs: ModifierOrAttrs<BasicTextElement> = null)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.huanshankeji.compose

import androidx.compose.runtime.Composable

/** A raw text composable which doesn't add any element on web. */
@Composable
expect fun RawText(text: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.huanshankeji.compose.layout

import androidx.compose.runtime.Composable
import com.huanshankeji.compose.ui.Element
import com.huanshankeji.compose.ui.ModifierOrAttrs

expect abstract class ColumnElement : Element
expect interface ColumnScope

@Composable
expect fun Column(modifierOrAttrs: ModifierOrAttrs<ColumnElement> = null, content: @Composable ColumnScope.() -> Unit)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.huanshankeji.compose.layout

import androidx.compose.runtime.Composable
import com.huanshankeji.compose.ui.Element
import com.huanshankeji.compose.ui.ModifierOrAttrs

expect abstract class RowElement : Element
expect interface RowScope

@Composable
expect fun Row(modifierOrAttrs: ModifierOrAttrs<RowElement> = null, content: @Composable RowScope.() -> Unit)
Original file line number Diff line number Diff line change
@@ -1,20 +1,78 @@
package com.huanshankeji.compose.ui

import com.huanshankeji.compose.ui.unit.SizeValue
//import androidx.compose.ui.Modifier
import com.huanshankeji.compose.ui.color.Color
import com.huanshankeji.compose.ui.unit.HeightOrWidth
import com.huanshankeji.compose.ui.unit.Length
import com.huanshankeji.compose.ui.unit.LengthOrPercentage

typealias NotNullModifierOrAttrs<TElement> = ModifierOrAttrsScope<TElement>.() -> Unit
typealias ModifierOrAttrs<TElement> = NotNullModifierOrAttrs<TElement>?

@Suppress("NOTHING_TO_INLINE")
inline fun <TElement : Element> modifierOrAttrs(noinline modifierOrAttrs: NotNullModifierOrAttrs<TElement>) =
modifierOrAttrs

operator fun <TElement : Element> NotNullModifierOrAttrs<TElement>.plus(other: ModifierOrAttrs<TElement>): ModifierOrAttrs<TElement> =
if (other === null) this
else {
{
this@plus()
other()
}
}

typealias ModifierOrAttrs<TElement> = (ModifierOrAttrsScope<TElement>.() -> Unit)?

expect abstract class Element
expect class ModifierOrAttrsScope<TElement : Element> {


expect class ModifierOrAttrsScope<out TElement : Element> {
fun style(builder: StyleScope.() -> Unit)
}

/**
* Keep in mind that the functions in this class call functions in
* [org.jetbrains.compose.web.css.StyleScope] and [androidx.compose.ui.Modifier] under the hood
* so their visual results are not consistent.
* As different orders of `Modifier` function calls produce different results,
* different orders of function calls in this class produce different results on desktop and Android.
* They do produce the same results on web as long as no former property is overriden by a latter one,
* as different orders of CSS properties do in the HTML `style` attribute.
*/
expect class StyleScope {
fun padding(value: SizeValue)
fun margin(value: Length)
fun height(value: HeightOrWidth)
fun width(value: HeightOrWidth)

fun backgroundColor(color: Color)

/**
* Currently inconsistent, adds inner border on desktop and Android and outer padding on web.
*/
fun platformBorder(width: Length, color: Color)

fun outerBorder(width: Length, color: Color)

fun roundedCornerOuterBorder(width: Length, color: Color, cornerRadius: LengthOrPercentage)
}

fun StyleScope.height(value: LengthOrPercentage) =
height(HeightOrWidth.Numeric(value))

fun StyleScope.width(value: LengthOrPercentage) =
width(HeightOrWidth.Numeric(value))

private const val PADDING_MESSAGE =
"This function is a placeholder for code completion. " +
"Use `margin` to add space around the composable, which is equivalent to `Modifier.padding`. " +
"Set `margin` in the inner composable to add inner padding."

@Deprecated(PADDING_MESSAGE)
fun StyleScope.padding(value: LengthOrPercentage): Unit =
throw NotImplementedError(PADDING_MESSAGE)

/*
/** An alternative immutable design like `Modifier`. */
/** An alternative immutable design like [Modifier]. */
expect class ModifierOrAttrsImmutable<T : Element> {
fun padding(): ModifierOrAttrsImmutable<T>
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.huanshankeji.compose.ui.color

expect class Color

internal fun UByte.alphaToFloatRatio(): Float =
toFloat() / 255

internal fun Float.alphaToUByte(): UByte {
require(this in 0f..1f)
return (this * 255).toInt().toUByte()
}

expect fun rgbaColor(red: UByte, green: UByte, blue: UByte, alpha: UByte): Color
expect fun rgbaColor(red: UByte, green: UByte, blue: UByte, alpha: Float): Color

expect fun rgbColor(red: UByte, green: UByte, blue: UByte): Color
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.huanshankeji.compose.ui.color

expect object Colors {
val black: Color
val white: Color
val gray : Color
val red: Color
val green: Color
val blue: Color
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
package com.huanshankeji.compose.ui.unit

expect abstract class SizeValue
expect class DpOrPxValue : SizeValue
// Percentage is only supported on JS.
expect sealed interface LengthOrPercentage
expect sealed interface Length : LengthOrPercentage
expect class Percentage : LengthOrPercentage

expect val Int.dpOrPx: DpOrPxValue
expect val Int.percent: Percentage

expect class DpOrPx : Length

expect val Int.dpOrPx: DpOrPx


sealed class HeightOrWidth {
class Numeric(val value: LengthOrPercentage) : HeightOrWidth()
object FillMax : HeightOrWidth()
object FitContent : HeightOrWidth()
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package com.huanshankeji.compose

import androidx.compose.runtime.Composable
import com.huanshankeji.compose.ui.ModifierOrAttrs
import com.huanshankeji.compose.ui.toAttrs
import org.jetbrains.compose.web.dom.Span
import org.jetbrains.compose.web.dom.Text
import org.w3c.dom.HTMLSpanElement

actual typealias BasicTextElement = HTMLSpanElement

@Composable
actual fun BasicText(text: String) =
Text(text)
actual fun BasicText(text: String, modifierOrAttrs: ModifierOrAttrs<HTMLSpanElement>) =
Span(modifierOrAttrs.toAttrs()) { Text(text) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.huanshankeji.compose

import androidx.compose.runtime.Composable
import org.jetbrains.compose.web.dom.Text

@Composable
actual fun RawText(text: String) =
Text(text)
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package com.huanshankeji.compose.layout
import androidx.compose.runtime.Composable
import com.huanshankeji.compose.ui.ModifierOrAttrs
import com.huanshankeji.compose.ui.toAttrs
import com.huanshankeji.compose.web.attributes.attrs
import com.huanshankeji.compose.web.attributes.plus
import com.huanshankeji.compose.web.css.FIT_CONTENT
import com.huanshankeji.compose.web.css.width
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.ElementScope
import org.w3c.dom.HTMLDivElement
Expand All @@ -18,4 +22,8 @@ actual interface BoxScope {

@Composable
actual fun Box(modifierOrAttrs: ModifierOrAttrs<BoxElement>, content: @Composable BoxScope.() -> Unit) =
Div(modifierOrAttrs.toAttrs()) { BoxScope.Impl(this).content() }
Div(attrs<BoxElement> {
style { width(FIT_CONTENT) }
} + modifierOrAttrs.toAttrs()) {
BoxScope.Impl(this).content()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.huanshankeji.compose.layout

import androidx.compose.runtime.Composable
import com.huanshankeji.compose.ui.ModifierOrAttrs
import com.huanshankeji.compose.ui.toAttrs
import org.jetbrains.compose.web.dom.ElementScope
import org.w3c.dom.HTMLDivElement

actual typealias ColumnElement = HTMLDivElement

actual interface ColumnScope {
val elementScope: ElementScope<HTMLDivElement>

class Impl(override val elementScope: ElementScope<HTMLDivElement>) : ColumnScope
}

@Composable
actual fun Column(modifierOrAttrs: ModifierOrAttrs<ColumnElement>, content: @Composable ColumnScope.() -> Unit) =
com.huanshankeji.compose.web.Column(modifierOrAttrs.toAttrs()) {
ColumnScope.Impl(this).content()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.huanshankeji.compose.layout

import androidx.compose.runtime.Composable
import com.huanshankeji.compose.ui.ModifierOrAttrs
import com.huanshankeji.compose.ui.toAttrs
import org.jetbrains.compose.web.dom.ElementScope
import org.w3c.dom.HTMLDivElement

actual typealias RowElement = HTMLDivElement

actual interface RowScope {
val elementScope: ElementScope<HTMLDivElement>

class Impl(override val elementScope: ElementScope<HTMLDivElement>) : RowScope
}

@Composable
actual fun Row(modifierOrAttrs: ModifierOrAttrs<RowElement>, content: @Composable RowScope.() -> Unit) =
com.huanshankeji.compose.web.Row(modifierOrAttrs.toAttrs()) {
RowScope.Impl(this).content()
}
Loading

0 comments on commit 8f04066

Please sign in to comment.