Skip to content

Commit fb88b42

Browse files
Farzad Tabashirmoallemi
authored andcommitted
[WEB-REACT] create webReact module
1 parent 99dc3e3 commit fb88b42

File tree

10 files changed

+285
-0
lines changed

10 files changed

+285
-0
lines changed

buildSrc/src/main/java/Dependencies.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ object Versions {
1616
const val recyclerView = "1.2.0-alpha05"
1717
const val glide = "4.11.0"
1818
const val logback = "1.2.3"
19+
const val react = "16.13.1-pre.110-kotlin-${kotlin}"
20+
const val styled = "1.0.0-pre.110-kotlin-${kotlin}"
1921

2022
object Android {
2123
const val minSdk = 21
@@ -89,4 +91,12 @@ object Dependencies {
8991
const val commonSerialization = "io.ktor:ktor-client-serialization:${Versions.ktor}"
9092
const val androidSerialization = "io.ktor:ktor-client-serialization-jvm:${Versions.ktor}"
9193
}
94+
95+
object KotlinWrappers {
96+
// Kotlin wrappers for popular JavaScript libraries
97+
const val react = "org.jetbrains:kotlin-react:${Versions.react}"
98+
const val reactDom = "org.jetbrains:kotlin-react-dom:${Versions.react}"
99+
const val styled = "org.jetbrains:kotlin-styled:${Versions.styled}"
100+
}
101+
92102
}

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ include(":androidApp")
2020
include(":shared")
2121
include(":server")
2222
include(":web")
23+
include(":webReact")
2324

2425
enableFeaturePreview("GRADLE_METADATA")
2526

webReact/build.gradle.kts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
plugins {
2+
kotlin("js")
3+
}
4+
5+
dependencies {
6+
implementation(kotlin("stdlib-js"))
7+
implementation(project(":shared"))
8+
9+
//React, React DOM + Kotlin Wrappers
10+
implementation(Dependencies.KotlinWrappers.react)
11+
implementation(Dependencies.KotlinWrappers.reactDom)
12+
implementation(npm("react", "16.13.1"))
13+
implementation(npm("react-dom", "16.13.1"))
14+
15+
//Styled + Kotlin Wrappers
16+
implementation(Dependencies.KotlinWrappers.styled)
17+
implementation(npm("styled-components", "~5.1.1"))
18+
implementation(npm("inline-style-prefixer", "~6.0.0"))
19+
20+
}
21+
22+
kotlin {
23+
js {
24+
useCommonJs()
25+
browser {
26+
runTask {
27+
outputFileName = "app.js"
28+
}
29+
}
30+
binaries.executable()
31+
}
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import component.AppList
2+
import kotlinx.coroutines.MainScope
3+
import kotlinx.coroutines.flow.launchIn
4+
import kotlinx.coroutines.flow.onEach
5+
import me.moallemi.kmpshowcase.shared.presentation.AppListViewModel
6+
import org.koin.core.KoinComponent
7+
import org.koin.core.inject
8+
import react.RProps
9+
import react.child
10+
import react.functionalComponent
11+
import react.useEffect
12+
import react.useState
13+
14+
class Application : KoinComponent {
15+
16+
private val appListViewModel: AppListViewModel by inject()
17+
18+
private val scope = MainScope()
19+
20+
fun createRootComponent() = functionalComponent<RProps> { _ ->
21+
22+
val (apps, setApps) = useState(appListViewModel.apps.value)
23+
appListViewModel.load()
24+
useEffect(dependencies = listOf()) {
25+
appListViewModel.apps.onEach { setApps(it) }.launchIn(scope)
26+
}
27+
28+
child(AppList) { attrs.apps = apps }
29+
30+
}
31+
32+
}

webReact/src/main/kotlin/Styles.kt

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import kotlinx.css.Align
2+
import kotlinx.css.CSSBuilder
3+
import kotlinx.css.Color
4+
import kotlinx.css.Display
5+
import kotlinx.css.FlexDirection
6+
import kotlinx.css.FlexWrap
7+
import kotlinx.css.JustifyContent
8+
import kotlinx.css.ObjectFit
9+
import kotlinx.css.alignSelf
10+
import kotlinx.css.backgroundColor
11+
import kotlinx.css.body
12+
import kotlinx.css.borderRadius
13+
import kotlinx.css.borderTopLeftRadius
14+
import kotlinx.css.borderTopRightRadius
15+
import kotlinx.css.color
16+
import kotlinx.css.display
17+
import kotlinx.css.flexDirection
18+
import kotlinx.css.flexWrap
19+
import kotlinx.css.fontFamily
20+
import kotlinx.css.fontSize
21+
import kotlinx.css.height
22+
import kotlinx.css.justifyContent
23+
import kotlinx.css.lineHeight
24+
import kotlinx.css.margin
25+
import kotlinx.css.objectFit
26+
import kotlinx.css.padding
27+
import kotlinx.css.pct
28+
import kotlinx.css.properties.boxShadow
29+
import kotlinx.css.properties.lh
30+
import kotlinx.css.px
31+
import kotlinx.css.width
32+
33+
object Styles {
34+
35+
val global: CSSBuilder.() -> Unit = {
36+
body {
37+
margin(0.px)
38+
padding(0.px)
39+
}
40+
}
41+
42+
val banner: CSSBuilder.() -> Unit = {
43+
width = 100.pct
44+
height = 120.px
45+
padding(vertical = 30.px)
46+
backgroundColor = Color("#4c49eb")
47+
}
48+
49+
val appList: CSSBuilder.() -> Unit = {
50+
display = Display.flex
51+
flexWrap = FlexWrap.wrap
52+
justifyContent = JustifyContent.center
53+
}
54+
55+
object Card {
56+
57+
val root: CSSBuilder.() -> Unit = {
58+
width = 200.px
59+
borderRadius = 5.px
60+
margin(vertical = 16.px, horizontal = 16.px)
61+
boxShadow(
62+
Color("#0000000d"),
63+
0.px,
64+
4.px,
65+
8.px,
66+
0.px,
67+
)
68+
}
69+
70+
val header: CSSBuilder.() -> Unit = {
71+
display = Display.flex
72+
flexDirection = FlexDirection.column
73+
width = 100.pct
74+
height = 100.px
75+
borderTopLeftRadius = 5.px
76+
borderTopRightRadius = 5.px
77+
backgroundColor = Color("#eaeaea")
78+
}
79+
80+
val headerImage: CSSBuilder.() -> Unit = {
81+
alignSelf = Align.center
82+
width = 80.pct
83+
height = 100.pct
84+
objectFit = ObjectFit.scaleDown
85+
}
86+
87+
val content: CSSBuilder.() -> Unit = {
88+
padding(vertical = 2.px, horizontal = 16.px)
89+
}
90+
91+
val contentTitle: CSSBuilder.() -> Unit = {
92+
fontFamily = "sans-serif"
93+
fontSize = 16.px
94+
color = Color("#000000")
95+
padding(vertical = 10.px)
96+
}
97+
98+
val contentDescription: CSSBuilder.() -> Unit = {
99+
fontFamily = "sans-serif"
100+
fontSize = 14.px
101+
color = Color("#888")
102+
lineHeight = 18.px.lh
103+
}
104+
105+
}
106+
107+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package component
2+
3+
import me.moallemi.kmpshowcase.shared.domain.model.App
4+
import react.RProps
5+
import react.child
6+
import react.functionalComponent
7+
import styled.css
8+
import styled.styledDiv
9+
10+
external interface AppListProps : RProps {
11+
var apps: List<App>
12+
}
13+
14+
val AppList = functionalComponent<AppListProps> { props ->
15+
styledDiv {
16+
css(Styles.appList)
17+
child(Banner)
18+
props.apps.forEach { item ->
19+
child(card) {
20+
attrs.name = item.name
21+
attrs.summary = item.summary
22+
attrs.bannerUrl = item.bannerUrl
23+
}
24+
}
25+
}
26+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package component
2+
3+
import react.RProps
4+
import react.functionalComponent
5+
import styled.css
6+
import styled.styledImg
7+
8+
private val bannerBase64 =
9+
"data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjMxMCIgdmlld0JveD0iMCAwIDc1NSAzMTAiIHdpZHRoPSI3NTUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxjbGlwUGF0aCBpZD0iYSI+PHBhdGggZD0ibTAgMGg0OTAuODYxdjk4aC00OTAuODYxeiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJiIj48cGF0aCBkPSJtMCAyMTJoNTExLjI5djk4aC01MTEuMjl6Ii8+PC9jbGlwUGF0aD48ZyBjbGlwLXBhdGg9InVybCgjYSkiPjxwYXRoIGQ9Im04My40MDQxIDg0LjM2MmM1LjAxMzkgNS4wNDc0IDEuNDM4OCAxMy42MzgtNS42NzU3IDEzLjYzOGgtNzcuNzI4NHYtOThoNzcuNzI4NWM3LjExNDQgMCAxMC42ODk1IDguNTkwNjQgNS42NzU2IDEzLjYzOGwtMzUuMTI3NCAzNS4zNjJ6IiBmaWxsPSIjZmY2YzBhIi8+PHBhdGggZD0ibTEwNC45NTIgNDljMC0yNy4wNjIgMjEuOTM4LTQ5IDQ5LTQ5aDE4MmMyNy4wNjIgMCA0OSAyMS45MzggNDkgNDkgMCAyNy4wNjItMjEuOTM4IDQ5LTQ5IDQ5aC0xODJjLTI3LjA2MiAwLTQ5LTIxLjkzOC00OS00OXoiIGZpbGw9IiMzODM5M2YiLz48cGF0aCBkPSJtMTU1LjA4IDc1aDUuNjg4di0xMy41MzZsMTAuNzI4LTEwLjg3MiAxOS4zNjggMjQuNDA4aDcuMTI4bC0yMi41MzYtMjguMzY4IDIxLjY3Mi0yMi4wMzJoLTcuNDE2bC0yOC45NDQgMzAuMDk2di0zMC4wOTZoLTUuNjg4em02Mi43NDYuODY0YzExLjIzMiAwIDE5LjUxMi04LjkyOCAxOS41MTItMTkuNDR2LS4xNDRjMC0xMC41MTItOC4yMDgtMTkuMjk2LTE5LjM2OC0xOS4yOTYtMTEuMjMyIDAtMTkuNDQgOC45MjgtMTkuNDQgMTkuNDR2LjE0NGMwIDEwLjUxMiA4LjEzNiAxOS4yOTYgMTkuMjk2IDE5LjI5NnptLjE0NC00Ljk2OGMtNy44NDggMC0xMy43NTItNi40OC0xMy43NTItMTQuNDcydi0uMTQ0YzAtNy44NDggNS41NDQtMTQuNCAxMy42MDgtMTQuNCA3Ljg0OCAwIDEzLjgyNCA2LjU1MiAxMy44MjQgMTQuNTQ0di4xNDRjMCA3Ljg0OC01LjYxNiAxNC4zMjgtMTMuNjggMTQuMzI4em0zOC45ODcgNC43NTJjMi42NjQgMCA0Ljc1Mi0uNTc2IDYuNjk2LTEuNTg0di00Ljc1MmMtMS45NDQuOTM2LTMuNTI4IDEuMjk2LTUuNCAxLjI5Ni0zLjc0NCAwLTYuMjY0LTEuNjU2LTYuMjY0LTYuMTkydi0yMS43NDRoMTEuODA4di00Ljg5NmgtMTEuODA4di0xMS4yMzJoLTUuNTQ0djExLjIzMmgtNS4xODR2NC44OTZoNS4xODR2MjIuNDY0YzAgNy41NiA0LjUzNiAxMC41MTIgMTAuNTEyIDEwLjUxMnptMTQuMjc3LS42NDhoNS41NDR2LTUyLjU2aC01LjU0NHptMTUuNjc0LTQ1LjM2aDYuMzM2di02LjEyaC02LjMzNnptLjM2IDQ1LjM2aDUuNTQ0di0zNy4yMjRoLTUuNTQ0em0xNS41MyAwaDUuNTQ0di0yMS4zMTJjMC02Ljk4NCA0Ljc1Mi0xMS42NjQgMTEuMTYtMTEuNjY0IDYuNTUyIDAgMTAuMjk2IDQuMzIgMTAuMjk2IDExLjIzMnYyMS43NDRoNS41NDR2LTIzLjExMmMwLTguODU2LTUuMjU2LTE0LjkwNC0xNC4yNTYtMTQuOTA0LTYuNDA4IDAtMTAuMjk2IDMuMjQtMTIuNzQ0IDcuMjcydi02LjQ4aC01LjU0NHoiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJtNDQxLjkwNiA5Ny45NTQ3YzI3LjAzNyAwIDQ4Ljk1NS0yMS45MTc3IDQ4Ljk1NS00OC45NTQ2cy0yMS45MTgtNDguOTU0Njg5OC00OC45NTUtNDguOTU0Njg5OC00OC45NTQgMjEuOTE3Nzg5OC00OC45NTQgNDguOTU0Njg5OCAyMS45MTcgNDguOTU0NiA0OC45NTQgNDguOTU0NnoiIGZpbGw9IiM2YjcwZmMiLz48L2c+PHBhdGggZD0ibTQ3LjEyNTEgMTEwLjExN2MzLjExNC01LjQ4OSAxMC42MzU4LTUuNDg5IDEzLjc0OTggMGw0NS45NzAxIDgxLjA0YzMuMjA2IDUuNjUyLS42NDMgMTIuODQzLTYuODc0OCAxMi44NDNoLTkxLjk0MDQyYy02LjIzMTg4IDAtMTAuMDgxMTQtNy4xOTEtNi44NzQ4Ni0xMi44NDN6IiBmaWxsPSIjNmI3MGZjIi8+PHBhdGggZD0ibTExNiAxNjkuOTFoMjMuNjA2YzIuODg5IDAgNS42NTkgMS4xNDcgNy43MDEgMy4xOSAyLjA0MyAyLjA0MyAzLjE5IDQuODEzIDMuMTkgNy43MDJ2MjMuMTUzaDI4LjkxNXYtMjMuMTQ4Yy0uMDAxLTEuNDMuMjgtMi44NDcuODI3LTQuMTcuNTQ3LTEuMzIyIDEuMzQ5LTIuNTIzIDIuMzYxLTMuNTM1IDEuMDExLTEuMDEyIDIuMjEyLTEuODE1IDMuNTM0LTIuMzYzIDEuMzIyLS41NDcgMi43MzgtLjgyOSA0LjE2OS0uODI5aDIzLjYwNnYtMjkuODE0aC0yMy42MDZjLTIuODg4IDAtNS42NTktMS4xNDgtNy43MDEtMy4xOS0yLjA0My0yLjA0My0zLjE5LTQuODEzLTMuMTktNy43MDJ2LTIzLjE1OWgtMjguOTE1djIzLjE1OWMwIDIuODg5LTEuMTQ3IDUuNjU5LTMuMTkgNy43MDItMi4wNDIgMi4wNDItNC44MTIgMy4xOS03LjcwMSAzLjE5aC0yMy42MDZ6IiBmaWxsPSIjNDVkYmI3Ii8+PHBhdGggZD0ibTIyMS45MDkgMTU1YzAtMjcuMDYyIDIxLjkzOC00OSA0OS00OWg0MzVjMjcuMDYyIDAgNDkgMjEuOTM4IDQ5IDQ5IDAgMjcuMDYyLTIxLjkzOCA0OS00OSA0OWgtNDM1Yy0yNy4wNjIgMC00OS0yMS45MzgtNDktNDl6IiBmaWxsPSIjMzgzOTNmIi8+PHBhdGggZD0ibTI3Mi4wMzcgMTgxaDUuNTQ0di00MC43NTJsMTguMzYgMjYuOTI4aC4yODhsMTguMzYtMjd2NDAuODI0aDUuNjg4di01MC40aC01Ljc2bC0xOC4zNiAyNy41MDQtMTguMzYtMjcuNTA0aC01Ljc2em03Mi4zNzIuNzkyYzYuNDA4IDAgMTAuMjI0LTMuMjQgMTIuNzQ0LTcuMjcydjYuNDhoNS40NzJ2LTM3LjIyNGgtNS40NzJ2MjEuMzEyYzAgNi45ODQtNC44MjQgMTEuNjY0LTExLjE2IDExLjY2NC02LjU1MiAwLTEwLjI5Ni00LjMyLTEwLjI5Ni0xMS4yMzJ2LTIxLjc0NGgtNS41NDR2MjMuMTEyYzAgOC44NTYgNS4yNTYgMTQuOTA0IDE0LjI1NiAxNC45MDR6bTI4LjMxMS0uNzkyaDUuNTQ0di01Mi41NmgtNS41NDR6bTI3Ljk4Ni42NDhjMi42NjQgMCA0Ljc1Mi0uNTc2IDYuNjk2LTEuNTg0di00Ljc1MmMtMS45NDQuOTM2LTMuNTI4IDEuMjk2LTUuNCAxLjI5Ni0zLjc0NCAwLTYuMjY0LTEuNjU2LTYuMjY0LTYuMTkydi0yMS43NDRoMTEuODA4di00Ljg5NmgtMTEuODA4di0xMS4yMzJoLTUuNTQ0djExLjIzMmgtNS4xODR2NC44OTZoNS4xODR2MjIuNDY0YzAgNy41NiA0LjUzNiAxMC41MTIgMTAuNTEyIDEwLjUxMnptMTMuOTE4LTQ2LjAwOGg2LjMzNnYtNi4xMmgtNi4zMzZ6bS4zNiA0NS4zNmg1LjU0NHYtMzcuMjI0aC01LjU0NHptMTUuNTMgMTEuNTJoNS41NDR2LTE4LjY0OGMyLjg4IDQuMTc2IDcuMzQ0IDcuOTIgMTQuMjU2IDcuOTIgOSAwIDE3Ljc4NC03LjIgMTcuNzg0LTE5LjM2OHYtLjE0NGMwLTEyLjI0LTguODU2LTE5LjI5Ni0xNy43ODQtMTkuMjk2LTYuODQgMC0xMS4yMzIgMy44MTYtMTQuMjU2IDguMjh2LTcuNDg4aC01LjU0NHptMTguNzkyLTE1LjY5NmMtNi45MTIgMC0xMy40NjQtNS43Ni0xMy40NjQtMTQuNHYtLjE0NGMwLTguNDk2IDYuNTUyLTE0LjMyOCAxMy40NjQtMTQuMzI4IDcuMDU2IDAgMTMuMTA0IDUuNjE2IDEzLjEwNCAxNC40di4xNDRjMCA5LTUuOTA0IDE0LjMyOC0xMy4xMDQgMTQuMzI4em0yNi42NDQgNC4xNzZoNS41NDR2LTUyLjU2aC01LjU0NHptMjYuNjE4Ljc5MmM2LjU1MiAwIDEwLjg3Mi0zLjAyNCAxMy40NjQtNi40MDh2NS42MTZoNS4zMjh2LTIyLjc1MmMwLTQuNzUyLTEuMjk2LTguMzUyLTMuODE2LTEwLjg3Mi0yLjczNi0yLjczNi02Ljc2OC00LjEwNC0xMi4wMjQtNC4xMDQtNS40IDAtOS40MzIgMS4yMjQtMTMuNTM2IDMuMDk2bDEuNjU2IDQuNTM2YzMuNDU2LTEuNTg0IDYuOTEyLTIuNzM2IDExLjMwNC0yLjczNiA2Ljk4NCAwIDExLjA4OCAzLjQ1NiAxMS4wODggMTAuMTUydjEuMjk2Yy0zLjMxMi0uOTM2LTYuNjk2LTEuNTg0LTExLjQ0OC0xLjU4NC05LjQzMiAwLTE1LjkxMiA0LjE3Ni0xNS45MTIgMTIuMDI0di4xNDRjMCA3LjYzMiA2Ljk4NCAxMS41OTIgMTMuODk2IDExLjU5MnptMS4wOC00LjM5MmMtNS4wNCAwLTkuMzYtMi43MzYtOS4zNi03LjQxNnYtLjE0NGMwLTQuNjggMy44ODgtNy43MDQgMTAuODcyLTcuNzA0IDQuNTM2IDAgOC4yMDguNzkyIDEwLjk0NCAxLjU4NHYzLjZjMCA1LjkwNC01LjYxNiAxMC4wOC0xMi40NTYgMTAuMDh6bTM4Ljc1MiA0LjI0OGMyLjY2NCAwIDQuNzUyLS41NzYgNi42OTYtMS41ODR2LTQuNzUyYy0xLjk0NC45MzYtMy41MjggMS4yOTYtNS40IDEuMjk2LTMuNzQ0IDAtNi4yNjQtMS42NTYtNi4yNjQtNi4xOTJ2LTIxLjc0NGgxMS44MDh2LTQuODk2aC0xMS44MDh2LTExLjIzMmgtNS41NDR2MTEuMjMyaC01LjE4NHY0Ljg5Nmg1LjE4NHYyMi40NjRjMCA3LjU2IDQuNTM2IDEwLjUxMiAxMC41MTIgMTAuNTEyem0xNS44NjEtLjY0OGg1LjU0NHYtMzIuMzI4aDExLjczNnYtNC43NTJoLTExLjgwOHYtMy4wMjRjMC01LjMyOCAyLjE2LTcuOTIgNi40OC03LjkyIDEuODcyIDAgMy4zODQuMzYgNS40LjkzNnYtNC44MjRjLTEuOC0uNjQ4LTMuNTI4LS45MzYtNi4xMi0uOTM2LTMuMzg0IDAtNi4xMiAxLjAwOC03Ljk5MiAyLjg4LTIuMDg4IDIuMDg4LTMuMjQgNS4zMjgtMy4yNCA5LjU3NnYzLjI0aC01LjExMnY0LjgyNGg1LjExMnptMzcuNzU5Ljg2NGMxMS4yMzIgMCAxOS41MTItOC45MjggMTkuNTEyLTE5LjQ0di0uMTQ0YzAtMTAuNTEyLTguMjA4LTE5LjI5Ni0xOS4zNjgtMTkuMjk2LTExLjIzMiAwLTE5LjQ0IDguOTI4LTE5LjQ0IDE5LjQ0di4xNDRjMCAxMC41MTIgOC4xMzYgMTkuMjk2IDE5LjI5NiAxOS4yOTZ6bS4xNDQtNC45NjhjLTcuODQ4IDAtMTMuNzUyLTYuNDgtMTMuNzUyLTE0LjQ3MnYtLjE0NGMwLTcuODQ4IDUuNTQ0LTE0LjQgMTMuNjA4LTE0LjQgNy44NDggMCAxMy44MjQgNi41NTIgMTMuODI0IDE0LjU0NHYuMTQ0YzAgNy44NDgtNS42MTYgMTQuMzI4LTEzLjY4IDE0LjMyOHptMjYuNTMxIDQuMTA0aDUuNTQ0di0xNC44MzJjMC0xMS4yMzIgNi42MjQtMTcuMDY0IDE0Ljc2LTE3LjA2NGguNDMydi01Ljk3NmMtNy4wNTYtLjI4OC0xMi40NTYgNC4xNzYtMTUuMTkyIDEwLjM2OHYtOS43MmgtNS41NDR6bTI2LjI5OSAwaDUuNTQ0di0yMS4yNGMwLTYuODQgNC40NjQtMTEuNzM2IDEwLjM2OC0xMS43MzZzOS41NzYgNC4xMDQgOS41NzYgMTEuMDg4djIxLjg4OGg1LjQ3MnYtMjEuNDU2YzAtNy4zNDQgNC42OC0xMS41MiAxMC4yOTYtMTEuNTIgNi4wNDggMCA5LjY0OCA0LjAzMiA5LjY0OCAxMS4yMzJ2MjEuNzQ0aDUuNTQ0di0yMy4wNGMwLTkuMjE2LTUuMjU2LTE0Ljk3Ni0xMy44MjQtMTQuOTc2LTYuNDggMC0xMC40NCAzLjMxMi0xMy4xMDQgNy40MTYtMi4wMTYtNC4xNzYtNS45MDQtNy40MTYtMTEuOTUyLTcuNDE2LTYuMjY0IDAtOS41NzYgMy4zODQtMTIuMDI0IDcuMDU2di02LjI2NGgtNS41NDR6IiBmaWxsPSIjZmZmIi8+PGcgY2xpcC1wYXRoPSJ1cmwoI2IpIj48cGF0aCBkPSJtMCAyNjFjMC0yNy4wNjIgMjEuOTM4LTQ5IDQ5LTQ5aDIxMWMyNy4wNjIgMCA0OSAyMS45MzggNDkgNDkgMCAyNy4wNjItMjEuOTM4IDQ5LTQ5IDQ5aC0yMTFjLTI3LjA2MTkgMC00OS0yMS45MzgtNDktNDl6IiBmaWxsPSIjMzgzOTNmIi8+PHBhdGggZD0ibTUwLjEyOCAyODdoNS41NDR2LTQwLjc1MmwxOC4zNiAyNi45MjhoLjI4OGwxOC4zNi0yN3Y0MC44MjRoNS42ODh2LTUwLjRoLTUuNzZsLTE4LjM2IDI3LjUwNC0xOC4zNi0yNy41MDRoLTUuNzZ6bTc1LjY4NC44NjRjMTEuMjMyIDAgMTkuNTEyLTguOTI4IDE5LjUxMi0xOS40NHYtLjE0NGMwLTEwLjUxMi04LjIwOC0xOS4yOTYtMTkuMzY4LTE5LjI5Ni0xMS4yMzIgMC0xOS40NCA4LjkyOC0xOS40NCAxOS40NHYuMTQ0YzAgMTAuNTEyIDguMTM2IDE5LjI5NiAxOS4yOTYgMTkuMjk2em0uMTQ0LTQuOTY4Yy03Ljg0OCAwLTEzLjc1Mi02LjQ4LTEzLjc1Mi0xNC40NzJ2LS4xNDRjMC03Ljg0OCA1LjU0NC0xNC40IDEzLjYwOC0xNC40IDcuODQ4IDAgMTMuODI0IDYuNTUyIDEzLjgyNCAxNC41NDR2LjE0NGMwIDcuODQ4LTUuNjE2IDE0LjMyOC0xMy42OCAxNC4zMjh6bTQ2LjMzMSA0Ljg5NmM5IDAgMTcuNzg0LTcuMiAxNy43ODQtMTkuMzY4di0uMTQ0YzAtMTIuMjQtOC44NTYtMTkuMjk2LTE3Ljc4NC0xOS4yOTYtNi44NCAwLTExLjIzMiAzLjgxNi0xNC4yNTYgOC4yOHYtMjIuODI0aC01LjU0NHY1Mi41Nmg1LjU0NHYtNy4xMjhjMi44OCA0LjE3NiA3LjM0NCA3LjkyIDE0LjI1NiA3Ljkyem0tMS4wMDgtNC45NjhjLTYuOTEyIDAtMTMuNDY0LTUuNzYtMTMuNDY0LTE0LjR2LS4xNDRjMC04LjQ5NiA2LjU1Mi0xNC4zMjggMTMuNDY0LTE0LjMyOCA3LjA1NiAwIDEzLjEwNCA1LjYxNiAxMy4xMDQgMTQuNHYuMTQ0YzAgOS01LjkwNCAxNC4zMjgtMTMuMTA0IDE0LjMyOHptMjYuMjg0LTQxLjE4NGg2LjMzNnYtNi4xMmgtNi4zMzZ6bS4zNiA0NS4zNmg1LjU0NHYtMzcuMjI0aC01LjU0NHptMTYuMDM0IDBoNS41NDR2LTUyLjU2aC01LjU0NHptMzEuODAyLjg2NGM3LjA1NiAwIDExLjU5Mi0yLjgwOCAxNS4zMzYtNi45ODRsLTMuNDU2LTMuMDk2Yy0zLjAyNCAzLjE2OC02LjYyNCA1LjI1Ni0xMS43MzYgNS4yNTYtNi42MjQgMC0xMi4zMTItNC41MzYtMTMuMTA0LTEyLjZoMjkuNTJjLjA3Mi0uNjQ4LjA3Mi0xLjA4LjA3Mi0xLjggMC0xMC44NzItNi4zMzYtMTkuNjU2LTE3LjM1Mi0xOS42NTYtMTAuMjk2IDAtMTcuODU2IDguNjQtMTcuODU2IDE5LjM2OHYuMTQ0YzAgMTEuNTIgOC4zNTIgMTkuMzY4IDE4LjU3NiAxOS4zNjh6bS0xMi45Ni0yMS41MjhjLjcyLTcuMjcyIDUuNjE2LTEyLjY3MiAxMi4wOTYtMTIuNjcyIDcuNDE2IDAgMTEuMzA0IDUuOTA0IDExLjg4IDEyLjY3MnoiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJtMzczLjE5MiAyMjAuOTU4IDMuODM5IDEyLjUzOSAxMi43NzItMi45NDVjMTIuMTEzLTIuNzkzIDIwLjUyNCAxMS43NzcgMTIuMDUxIDIwLjg2NGwtOC45NCA5LjYwNSA4Ljk0IDkuNTg4YzguNDczIDkuMDk5LjA2MiAyMy42NjMtMTIuMDUxIDIwLjg3MWwtMTIuNzcyLTIuOTQ2LTMuODM5IDEyLjUxMWMtMy42MzQgMTEuODgtMjAuNDcyIDExLjg4LTI0LjA5NSAwbC0zLjgzOC0xMi41MTEtMTIuNzczIDIuOTQ2Yy0xMi4xMTMgMi43OTItMjAuNTIzLTExLjc3OC0xMi4wNS0yMC44NzFsOC45NC05LjU4OC04Ljk0LTkuNTkzYy04LjQ3My05LjA5OS0uMDYzLTIzLjY1NyAxMi4wNS0yMC44NjVsMTIuNzczIDIuOTQ2IDMuODM4LTEyLjU0YzMuNjM0LTExLjg5NiAyMC40NjEtMTEuODk2IDI0LjA5NS0uMDExeiIgZmlsbD0iIzZiNzBmYyIvPjxyZWN0IGZpbGw9IiM0NWRiYjciIGhlaWdodD0iOTgiIHJ4PSI4IiB3aWR0aD0iOTgiIHg9IjQxMy4yOSIgeT0iMjEyIi8+PC9nPjwvc3ZnPg=="
10+
11+
12+
val Banner = functionalComponent<RProps> {
13+
styledImg(src = bannerBase64) {
14+
css(Styles.banner)
15+
}
16+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package component
2+
3+
import react.RProps
4+
import react.functionalComponent
5+
import styled.css
6+
import styled.styledDiv
7+
import styled.styledImg
8+
9+
external interface CardProps : RProps {
10+
var name: String
11+
var summary: String
12+
var bannerUrl: String?
13+
}
14+
15+
val card = functionalComponent<CardProps> { props ->
16+
styledDiv {
17+
css(Styles.Card.root)
18+
styledDiv {
19+
css(Styles.Card.header)
20+
styledImg(src = props.bannerUrl, alt = "HeaderImage") {
21+
css(Styles.Card.headerImage)
22+
}
23+
}
24+
styledDiv {
25+
css(Styles.Card.content)
26+
styledDiv {
27+
css(Styles.Card.contentTitle)
28+
+props.name
29+
}
30+
styledDiv {
31+
css(Styles.Card.contentDescription)
32+
+props.summary
33+
}
34+
}
35+
}
36+
}

webReact/src/main/kotlin/main.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import kotlinx.browser.document
2+
import me.moallemi.kmpshowcase.shared.di.initKoinJs
3+
import react.dom.render
4+
import react.child
5+
import styled.injectGlobal
6+
7+
fun main() {
8+
initKoinJs()
9+
injectGlobal(Styles.global)
10+
val app = Application()
11+
render(document.getElementById("root")) {
12+
child(app.createRootComponent())
13+
}
14+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Kotlin Multiplatform Showcase</title>
6+
</head>
7+
<body>
8+
<div id="root"></div>
9+
<script src="app.js"></script>
10+
</body>
11+
</html>

0 commit comments

Comments
 (0)