-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Some components have {component}.children but others have to fall back to prop.children? #20
Comments
Thanks, this should be fixed in the MUI docs: mui/material-ui#17674 Will re-generate when it's fixed. |
Actually, there is no problem here. As indicated by mui/material-ui#17674 (comment), most MUI components forward "unknown" props to root elements. This is explicitly documented in the MUI API, and also in the Feliz.MaterialUI bindings: The semantically correct usage is therefore: let render (state: State) (dispatch: Msg -> unit) =
Mui.container [
container.maxWidth.xl
container.children [
Mui.card [
card.raised false
paper.children [
Mui.cardHeader [ ]
Mui.cardContent [ ]
]
]
]
] Feel free to close if you agree. |
Although it might be the "correct" usage, the propagation of children into the very top container type is (and should be) an implementation detail: it will confuse many users having to know the specifics of this. My point is that we want to make looking for things (discoverability) as simple as possible without having to look in too many places, in this case when you are using This also applies to things like |
I assume you don't want each component to separately define ALL the props of its root elements, and its root elements' root elements, and so on, all the way to native elements (essentially replicating the entire Feliz API for all components). Under that assumption:
I like discoverability as much as the next dev (hence my working on Feliz.MaterialUI), but at the moment I'm inclined to think that when using Material-UI, it's simply required knowledge (and easily accessible) which components will forward props to which other components. It is certainly required when working with MUI in JS/TS. That said, if we can improve upon that without too many caveats, I'm open for that. Furthermore, since things are auto-generated, it wouldn't be too hard to insert prop X from component Y into component Z, too, as long as we have pre-defined lists of which props to insert (or can infer that from the source docs somehow). It would be a bit of work to implement it, but once implemented, it would "just work". I would also need to make a solution for inserting native Feliz props. |
When working with Mui in JS, you write the code <Card>
<CardHeader>Title</CardHeader>
<CardContent>Content</CardContent>
</Card> You don't think about children being forwarded to paper - I certainly don't as I just learned this detail about it and if I had to learn and remember where what goes, then it already too high of an entry barrier for me
That would actually be ideal, if it doesn't add to the bundle size and can be added to the generator, then I am all in for it. I think of Mui as a UI library that worked around inheritance by forwarding the props up to the parent but many UI libraries allow you to set properties of the specific element (i.e. text of button) because it inherits from a generic control which has text as well, since we can't use inheritence here (I have no idea how in any case) then duplicating the code might be a solution
Can we hard code the inheritance hierarchy in a list and use that as a starting point? it should be doable, there aren't that many components after all |
You're right,
Hm. Something to actually consider. 🤔 But replicating the entire Feliz UI? Then I'd have to re-generate every time Feliz changed, too. Added maintenance burden. Side note: Since you're in favour of this, why not have the same kind of API for Feliz? There are, after all, several element-specific props for native components, too. (We've touched on this in a few issues in the Feliz repo.)
Check out the Elmish.WPF.Dynamic usage. Something like that might work, but I'm not sure if the API is necessarily better (it might be). It would also not be immediately compatible with vanilla Fable.React, though a simple helper function could solve that. Feel free to experiment!
That is certainly possible, though if feasible I'd rather just parse the hierarchy from the previously mentioned prop forwarding message. |
Feliz props are static, they only change when the html specs change so that is not a big deal
Unlike elements in third-party libraries, Html elements are not specialized (with some exceptions ofc) so I am not sure this code would make sense Html.div [
div.style [ style.margin 5 ]
div.children [
Html.button [
button.style [ style.padding 10 ]
button.onClick (fun _ -> printfn "Hello")
button.text "Click me"
]
]
] Though if I look at it now, it is not bad at all. However, 3rd party libraries would have to consider extending existing props such
I don't think I like that syntax, it is not as "declarative" as I would like to have it (though this is very subjective)
If it is possible that would be great, I see a couple of components say: "Any other props supplied will be provided to the root element (native element)" but it is not clear (from my understanding) which element is the native element of that specific component (such as |
Assuming the Feliz API is stable, which is might not be. "Can you please add overload X", "you've forgotten prop Y", etc. 😅 Stable binding target API doesn't mean stable binding API 😉
Yeah, if it says "native element" then I guess we could just copy the whole Feliz API. Just had a brainwave: Would it work if instead of modules and static types, we have normal classes, and the type paper () =
member inline __.X ...
member inline __.Y ...
let paper = paper ()
type card () =
inherit paper ()
member inline __.Z ...
// inherits X and Y
let card = card () |
Looks like that would work 😉 |
That solves our problems then, doesn't it? If you do that in Feliz (e.g. a Heck, you could even easily have component-specific props using that method. As a first step, all component-specific prop classes can derive from a Edit: That won't work though, because MUI often allows you to change the underlying component (e.g. Would the same |
Another complication for the inheritance stuff: According to mui/material-ui#17674 (comment), there are components that don't support all of their "root" props. For example, not all components support Personally I don't like the idea of having |
It seems that MUI never forwards |
There is another problem with inheritable prop classes: they cannot be extended with equivalent modules, this doesn't compile 😢 open Fable.Core
[<Erase>]
type paperType() =
member inline _.X = "one"
member inline _.Y = "two"
let paper = paperType()
[<Erase>]
type cardType() =
inherit paperType()
member inline _.Z = "three"
let card = cardType ()
module card =
let hello = "whatever"
// card does not have hello definition
let xs = [card.X; card.Y; card.Z; card.hello]
printfn "%A" xs |
No, but this does 😄 open Fable.Core
[<Erase>]
type paperType() =
member inline _.X = "one"
member inline _.Y = "two"
let paper = paperType()
[<Erase>]
type cardHelloType() =
member inline _.foo = "whatever"
[<Erase>]
type cardType() =
inherit paperType()
let cardHelloType = cardHelloType ()
member inline _.Z = "three"
member inline _.hello = cardHelloType
let card = cardType ()
// card does indeed have hello definition
let xs = [card.X; card.Y; card.Z; card.hello.foo]
printfn "%A" xs |
Nice! |
PS, I'd consider naming the type |
Regarding my previous comment:
In fact, we might yet get it working. We could parametrize the prop types by the root component's prop type, e.g. Not ideal, but an idea nonetheless. Regarding another previous comment of mine:
One workaround is to shadow inherited, unsupported |
Re. the Elmish.WPF.Dynamic syntax: A slightly better syntax in the context of Feliz may be the following: Html.div (fun d ->
d.style (fun s -> s.margin 5)
d.children [
Html.button (fun b ->
b.style (fun s -> s.padding 10)
b.onClick (fun _ -> printfn "Hello")
b.text "Click me"
)
]
) In other words, the usage is exactly like the current syntax except instead of a prop list, there's an initializer function that gives you the parameter by which you access the props. So you never have to "hunt around" for the correct type/module where the available props are defined. This may have more benefits in Feliz.MaterialUI than in Feliz. For example, all the
That instruction could be moot if one simply did appBar.classes (fun c ->
c.root "foo"
c.positionFixed "bar"
)
// Existing syntax for comparison
appBar.classes [
classes.appBar.root "foo"
classes.appBar.positionFixed "bar"
] In any case, I'm not about to go making this change unless you really like it and would use it in Feliz. It has benefits and drawbacks. I just thought I'd mention it. |
Dang, I just thought of something. With the proposed instance-member syntax, it's not possible to have mixed enum and non-enum props. For example, I can't think of a way to achieve both of the following variants using instance members, because popover.anchorOrigin.topLeft
popover.anchorOrigin (10, 20) The best I can come up with right now is to make the property a parameterless method, or use different names: popover.anchorOrigin().topLeft
popover.anchorOrigin' (10, 20) I'm not particularly fond of either, though if I had to choose between these I think I like the different name better than the property-as-method. Still, I would very much like to enable inheritance, otherwise I doubt I'll fully implement the suggestions in this issue (I don't want to duplicate the entire Feliz API for each and every component). Ideas welcome! |
Man, this issue is proving difficult. I was hovering over the "commit" button with a complete solution, implemented in the generator and all, but figured I'd test it first just to be sure. And it turns out that I can't use this to inherit enum (module) props, because module aliases are only accessible within a file. I tried doing the following ( [<AutoOpen>]
module InheritanceLevel2 =
type menuItem = buttonBaseProps
module menuIteml = buttonBaseProps
[<AutoOpen>]
module InheritanceLevel1 =
type menuItem = listItemProps
type listItem = buttonBaseProps
module menuItem = listItemProps
module listItem = buttonBaseProps
[<AutoOpen>]
module InheritanceLevel0 =
type menuItem = menuItemProps
type listItem = listItemProps
type buttonBase = buttonBaseProps
module menuItem = menuItemProps
module listItem = listItemProps
module buttonBase = buttonBaseProps This works great for the prop types, but not the modules, whose aliases are not accessible outside this file. So I'm back to start, really. I could duplicate all props in the types/modules that inherit them, but that won't work for 3rd party inheritance (e.g. when inheriting base Feliz props) because I don't have those definitions (also, there's a LOT of them, and they would be present for all 120 MUI components, so compile times would likely be horrendous). I'd like, if possible, to have a solution where components can inherit props from 3rd party components (such as Feliz). |
Continuing discussion from Zaid-Ajaj/Feliz#54 (comment):
Not a bad idea. Though I still haven't warmed entirely to the idea of duplicating. As I see it, there are two issues at work here: Base Feliz propsIf only a few properties are used frequently, and the rest of the properties are used rarely, then it could work well to duplicate only the frequently used base props for each MUI component. However. I worry (perhaps prematurely?) that it may not be easy to choose the frequently used props (for example, in addition to your list, all button-like elements may have Furthermore, if prop overloads should be improved in Feliz (perhaps not that likely?), those changes should be done in Feliz.MaterialUI as well (otherwise users will have to use Inheritance within MUIHere I have full control over prop definitions and can easily duplicate (and even add "Inherited from X" to the docs for each property). So this isn't as much of an issue. |
I have a slight twist on the latest idea (two comments up) I think may actually work. The only trouble I had were with enum prop modules. Since we can't alias the modules, we can alias the prop types inside the modules instead. Seems like that'll have to work. I'll get back with details once I have tried. If it works, the only thing that'll need to be hardcoded/duplicated from Feliz is the list of type names inside the |
Okay, I made it! 0.4.0 is in the pipeline. Even just aliasing all inherited enum prop types (e.g. Feliz.MaterialUI must then be updated each time a new enum prop type (a new type in the I chose to alias Give it a try 😃 Thanks for pushing me on this until I was too nerd-sniped to give it up! 😆 |
Maaan, now even F# is working against me. It seems I am pushing type aliasing past its limits. 😒 The code is correct and works sometimes (e.g. from the same file, or with project references), but other times (e.g. from another file, or from the 0.4.0 NuGet package) certain random props aren't available. I think this puts an end to all attempts at "inheritance" for static types using type aliasing. There are four options left:
Again, I have not entirely warmed to duplicating arbitrary (or all) base API props. I have unlisted the 0.4.0 NuGet package. |
- Disable native prop inheritance - Duplicate inherited MUI props in child components - More info: #20 (comment)
0.5.0 is in the pipeline with MUI prop inheritance (duplication), but no native DOM prop duplication. That should be done on a component-by-component and prop-by-prop basis as desired, if at all. I have no immediate plans of adding native DOM props, but I'm open to suggestions. |
I am gonna give 0.5.0 a try very soon and let you know of my findings! Thanks a lot for keeping an open mind for the possible options out there |
|
Also I loved the nerd-sniping reference ❤️ |
I think it would be clean and consistent if
children
prop was added to all components so that you don't get this:but instead
The text was updated successfully, but these errors were encountered: