You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
3.7 KiB
149 lines
3.7 KiB
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) |
|
} |
|
} |