compose-for-web-demos
                                
                                
                                
                                    compose-for-web-demos copied to clipboard
                            
                            
                            
                        Events customization
Currently only onClick event could be passed as parameter. We need to support all other events as well, and it does not seem it is possible to pass them all as parameters. I would also like to have events block in the composable body. Like:
Button{
    events{
        onClick = { value-- }
    }
    css{
        bgColor = "red"
    }
    Text(value.toString()){
        css{
            font = "Arial"
        }
    }
}
                                    
                                    
                                    
                                
Compose (on Android) achieves this behavior using Modifier api. Although a bit weird from the start, (was critical myself), it actually applies to idea of composables much better than blocks like that.
One of ideas that compose supports is addition of custom functions for grouping elements without introducing additional layout wrappers (html elements in this case):
@Composable
fun MyBusinessLogicElement() {
    // Doesn't add a new html element apart from those defined below
    div() { /*something*/ }
    button() { /*something */ }
}
Here, with modifier api, we can provide customization for different elements separately with a single parameter (sometimes even scoped so that it is typed):
@Composable
fun MyBusinessLogicElement(
    layoutModifier: LayoutModifier
    buttonModifier: ButtonModifier
) {
    div(layoutModifier) { /*something*/ }
    button(buttonModifier) { /*something */ }
}
We can achieve a similar effect with blocks and typed receivers, but I noticed that without multireceiver functions it was limiting expressiveness quite significantly.
Apart from that, it feels that leaning closer to Android api will help people jump in and adapt quicker, but not sure how much of the factor it will be.
I did some research on HTML related API in compose before (albeit from the server side), and results can he found here.
Providing styles and events via modifiers could be really ugly, but we can actually work around that in the following way:
class StyledButtonProps{
  fun css(builder: CssBuilder.()->Unit){...}
  fun props(builder: ButtonProps.()->Unit){}
  fun children(@Composable ()->Unit){...}
}
@Composable fun StyledButton(props: StyledButtonProps.()->Unit){
  val props = StyledButtonProps().apply(props)
  Button(props.style, props.props){
    props.children()
  }
}
                                    
                                    
                                    
                                
Yeah, long chain of modifiers is not the best way of expressing many things (been there, done that 😅). However, they are easily extractable and chainable without much hassle. Anyways, the fact that we should keep APIs among the platforms aligned still stands as one of the major targets from my perspective, so maybe we could find a compromise built on top of primitives already existing in Compose 🙂 .
I like the idea of DOM node metadata builder as more structured way presented above, which makes it probably more discoverable for the end user and more familiar to those who use react a lot. Modifiers can be scary whenever you start to tweak a lot of things on each node, so these builders can actually provide a way to structure those things much better.
Apart from that, I don't quite see direct benefit of hiding everything behind the props the way React does in Kotlin. With the better type system, we can rely on function parameters to convey all the information while hiding metadata for the DOM nodes inside (maybe props, maybe modifiers). For example, children param or text for the button can be completely separated from props so that people will know what is required for the component to operate correctly. Leland mentioned this couple of times in his talks, function parameters are the props of Compose and they can be efficiently optimized by compiler to skip over and check whether they are changed.
At the same time, separating css, props (maybe something else, I am thinking at least canvas operations or accessing HTMLElement attributes from JS) can expand the list of required fields quite a lot. If we are looking from the point of extending these APIs in the future, I feel like modifiers provide somewhat more uniform access, which allows to change implementation details only. For example:
@Composable
fun MyButton() {
     Button(Modifier.className("my-button"))
}
@Composable
fun Button(modifier: Modifier) {
     Element("button", modifier)
}
@Composable
fun Element(tag: String, modifier: Modifier) {
     // apply modifiers
}
If modifiers were to apply somehow differently (e.g. now we want to set className through HTMLElement rather than attributes), we would change only the way of how modifiers are applied (or even better, only modifier body). The rest of the API will stay the same.
One additional benefit of modifiers is that they are cheaper at runtime. You don't need to materialize the whole chain, it is enough to go through each element in the linked list (basically each modifier is one of those) and apply them to the DOM node.
Overall, I am pretty sure that we could create a way of providing modifiers in much more structured way than compose currently does on Android. However, there obviously should be a discussion to check alternatives (maybe we could involve someone from Compose team to chime in as well :)).
From the top of my head, I am thinking about something like:
@Composable fun StyledButton(text: String) {
    Button(
        text = text, 
        modifier = Modifier
             .css { 
                   background = Color.White // inline css
             }
             .attributes {
                   className = "styled-button" // html property
             }
             ...
    )
}
                                    
                                    
                                    
                                
React props are not the kotlin way because they are part of how untyped JS world works. But providing everything via modifiers is not Kotlin way as well. For now, it seems like can do a compromise and create low-level components with modifiers chains and then add nice modifier builders on top of them like the way I've showed above. This way we can keep low-level access for component developers and concise syntax for users. The user-side wrappers could be done as extensions in a separate library like tornado or kotlin-styled works.