google / mesop

Rapidly build AI apps in Python
https://google.github.io/mesop/
Apache License 2.0
5.66k stars 276 forks source link

Add Material tab component #93

Open wwwillchen opened 8 months ago

wwwillchen commented 8 months ago

https://material.angular.io/components/tabs/overview

wwwillchen commented 7 months ago

Discussed API with @richard-to:

with me.tabs():
  with me.tab("tab1"):
     me.text("hi")
  with me.tab("tab2"):
     me.text("hi")

This seems better than the more implicit alternative syntax of:

with me.tab("1"):
  me.text("a")
with me.tab("2"):
  me.text("b") 

Because what happens if there's something in-between the tabs?

with me.tab("1"):
  me.text("a")
me.text("does this split the tabs into two tab groups?")
with me.tab("2"):
  me.text("b") 
richard-to commented 3 weeks ago

I tried an initial implementation of this, but tabs not rendering. Wondering if the issue is due to the component renderer wrapper around the mat-tab. But haven't looked into it too closely yet. Not getting any error messages in the chrome dev console.

richard-to commented 2 weeks ago

So I think the issue is that the tab group component will query explicitly for the MatTab component which is why it can't find anything.

See: https://github.com/angular/components/blob/main/src/material/tabs/tab-group.ts#L105

 _tabs: QueryList<MatTab> = new QueryList<MatTab>();

I think if we can inherit the MatTabGroup class change how the tabs are selected, it could be possible to make the tabs render in Mesop. But haven't tested it out yet. Still looking into it.

Although I guess it should be getting the descendants:

@ContentChildren(MatTab, {descendants: true}) _allTabs: QueryList<MatTab>;

But later I guess they do some filtering for the closest tab group parent or something.

richard-to commented 2 weeks ago

Still thinking of how best to get the MatTab instances. The main problem is that Component Renderer makes it hard to QueryList since it can't dig inside of the template of another component.

Also, there's the issue that I can't import Component Renderer into the Tabs component since it would create a circular dependency based on how our current BUILD rules are constructed.

Currently I think the simplest option is to create a TabRegistrationService that is shared by Tabs / Tab components. For this to work, however, the TabRegistration needs to know how to get the right child tabs. This could be done by including an "id" in the tabs component and tab component. However, it would be better if the user did not have to explicitly specify the id. So it would be good if we could implicitly generate an id from the Tabs component and pass that down its children. However, I don't think that's easy to do since it is a slot, so we don't know what will be there. So we'd need a way to generate the id and associate

This is also dependent on if we can extended MatTabGroup component and override how _allTabs is set. I believe this should be possible.

richard-to commented 2 weeks ago

Maybe another option is if we are able to add multiple named ng-content placeholders. That could be another option I think.

Here I was thinking that tab would not be an actual component but some kind of special syntactic sugar for the generic named ng-content placeholder.

But I guess the issue would be needing stuff like labels and other parameters on the tab. So may be wouldn't work.

with tabs():
  with tab():  // not a really component, but a wrapper for some generic named ng-content placeholder.
    me.text()
  with tab():
    me.text()