forked from JetBrains/compose-multiplatform
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathComposeInReactApp.kt
149 lines (128 loc) · 3.71 KB
/
ComposeInReactApp.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import org.jetbrains.compose.web.dom.Div
import org.jetbrains.compose.web.dom.Text
import org.jetbrains.compose.web.renderComposable
import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.css.*
import kotlinx.html.InputType
import kotlinx.html.js.onClickFunction
import kotlinx.html.js.onInputFunction
import org.w3c.dom.HTMLElement
import react.*
import react.dom.*
import styled.css
import styled.styledDiv
@Composable
private fun ComposableComponentToUseInReact(count: State<Int>) {
repeat(count.value) {
Div {
Text("Item $it")
}
}
}
/**
* @param containerRef - [RMutableRef] - reference to the HTMLElement that is used as a root for Composition
* @param stateInitialValue - initial state value for the Composition
* @param stateValueProvider - a lambda that's used to change the state's value
* @param composable - the content controlled by Compose and mounted in a root provided by [containerRef]
*/
private fun <T> useCompose(
containerRef: RMutableRef<HTMLElement>,
stateInitialValue: T,
stateValueProvider: () -> T,
composable: @Composable (state: State<T>) -> Unit
) {
val mutableState = useRef(mutableStateOf(stateInitialValue))
useEffect {
mutableState.current?.value = stateValueProvider()
}
useLayoutEffectWithCleanup(dependencies = emptyList()) {
val composition = renderComposable(containerRef.current!!) {
composable(mutableState.current!!)
}
return@useLayoutEffectWithCleanup {
composition.dispose()
}
}
}
private external interface ListProps : RProps {
var countOfItems: Int
}
private val composeListComponentWrapper = functionalComponent<ListProps> { props ->
val containerRef = useRef<HTMLElement>(null)
useCompose(
containerRef = containerRef,
stateInitialValue = 0,
stateValueProvider = { props.countOfItems }
) {
ComposableComponentToUseInReact(it)
}
// This div will be a root for the Composition managed by Compose
div { ref { containerRef.current = it } }
}
private val column = functionalComponent<RProps> {
val (counter, setCounter) = useState(0)
styledDiv {
css {
padding = "25px"
}
h3 {
+"Update items count using slider:"
}
input(type = InputType.range) {
attrs {
onInputFunction = {
setCounter(it.target?.asDynamic().value.toString().toInt())
}
value = "$counter"
}
}
h3 {
+"Compose controlled items:"
}
child(composeListComponentWrapper) {
this.attrs {
countOfItems = counter
}
}
}
}
private val appContent = functionalComponent<RProps> {
val (columnsCount, setColumnsCount) = useState(3)
a(href = "${window.location.origin}?app=composeApp") {
+"GO TO REACT IN COMPOSE EXAMPLE"
}
button {
attrs {
onClickFunction = {
setColumnsCount(columnsCount - 1)
}
}
+"Remove column"
}
button {
attrs {
onClickFunction = {
setColumnsCount(columnsCount + 1)
}
}
+"Add column"
}
styledDiv {
css {
display = Display.flex
flexDirection = FlexDirection.row
}
repeat(columnsCount) {
child(column)
}
}
}
fun composeInReactAppExample() {
render(document.getElementById("root")) {
child(appContent)
}
}