pipalacademy / kutty

Reusable HTML widgets for building web apps in pure Python
MIT License
3 stars 0 forks source link

kutty-bootstrap: TabView component #26

Open nikochiko opened 1 year ago

nikochiko commented 1 year ago

Should have this behaviour: https://getbootstrap.com/docs/4.6/components/navs/#javascript-behavior

image

The TabView component should allow creating component with a tablist and different content associated with each tab in the tablist. The content of the "tab panel" should change based on which tab is selected.

Sample usage:

from kutty.bootstrap import TabView

tabs = {
    "Alice": "Alice likes Foo",
    "Bob": "Bob likes Bar",
    "Charlie": "Charlie likes Baz",
}
tab_view = TabView()
for title, content in tabs.items():
  tab_view.add_tab(title, content)

If user wants more control, they can use tab_view.tab_list and tab_view.tab_panel.

anandology commented 1 year ago

How will you support icons or badges in the title?

Something like: https://getbootstrap.com/docs/4.6/components/badge/#pill-badges

nikochiko commented 1 year ago

How about if the title need not be text?

We could something like this:

tab_view = TabView()

for title, content in tabs.items():
    tab_view.add(Pill(title), content))

Same goes for content too.

anandology commented 1 year ago

Wouldn't it be better to have a separate TabPane class that contains both the title and the body?

That way, you can do:

tabs = TabView()
tabs.add_tab("Home", home())

which is same as:

tabs = TabView()
tabs.add(TabPane("Home", home()))

The TabPane can support extra options like pills, icons etc.

nikochiko commented 1 year ago

The tab title and body both need to go into separate divs. Also, I think my solution was not correct. There are separate classes for each nav style. nav-tabs and nav-pills are just two, and both have to be added on the tab list and not individual tabs. I think a style_as="tabs" or style_as="pills" might be more consistent here.

On Wed, Feb 1, 2023, 12:42 PM Anand Chitipothu @.***> wrote:

Wouldn't it be better to have a separate TabPane class that contains both the title and the body?

That way, you can do:

tabs = TabView() tabs.add_tab("Home", home())

which is same as:

tabs = TabView() tabs.add(TabPane("Home", home()))

The TabPane can support extra options like pills, icons etc.

— Reply to this email directly, view it on GitHub https://github.com/pipalacademy/kutty/issues/26#issuecomment-1411568845, or unsubscribe https://github.com/notifications/unsubscribe-auth/AI7MKYPWMQYT4O3NIP4URBTWVIEHVANCNFSM6AAAAAAUNKGK2U . You are receiving this because you authored the thread.Message ID: @.***>

anandology commented 1 year ago

The tab title and body both need to go into separate divs. Also, I think my solution was not correct. There are separate classes for each nav style.

What you have mentioned is just an interface. The implementation can decide to render the title and body in separate divs.

nikochiko commented 1 year ago

What you have mentioned is just an interface. The implementation can decide to render the title and body in separate divs.

For the components so far, we create special methods for adding elements like this conveniently, but each component itself renders only one output and that is directly added to the parent. It would be powerful if we could figure out a way to render a child differently based on its parent.

anandology commented 1 year ago

Rendering starts at the parent and parent decides how to render the children. So the parent component has complete control over how to render the children, either as a single div or in two different places.

On Wed, Feb 1, 2023, 4:25 PM Kaustubh Maske Patil @.***> wrote:

What you have mentioned is just an interface. The implementation can decide to render the title and body in separate divs.

For the components so far, we create special methods for adding elements like this conveniently, but each component itself renders only one output and that is directly added to the parent. It would be powerful if we could figure out a way to render a child differently based on its parent.

— Reply to this email directly, view it on GitHub https://github.com/pipalacademy/kutty/issues/26#issuecomment-1411862086, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAB3EKJK3ZBCSWETZ33QFTWVI6K7ANCNFSM6AAAAAAUNKGK2U . You are receiving this because you commented.Message ID: @.***>

nikochiko commented 1 year ago

But the render method would be on the child itself and it can only render to HTML text. If we modify the parent's render method to take out the tab and content as attributes from the child, then that should be a new kind of component because sometimes it will be rendered with its render and sometimes with its parent's render.

anandology commented 1 year ago

Why not something like this?


class TabView:
    def __init__(self):
        self.panes = []

    def render(self):
        a = self.render_tabs()
        b = self.render_content()
        return html.div(a, b).render()

    def render_tabs(self):
        tabs = [pane.render_tab() for pane in self.panes]
        return html.ul(*tabs, class_="nav nav-tabs", role="tablist")

    def render_content(self):
        contents = [pane.render_content() for pane in self.panes]
        return html.div(*contents, class_="nav-content")

class TabPane:
    def __init__(self, title, content):
        self.title = title
        self.content = content

    ...
nikochiko commented 1 year ago

Because it doesn't store panes in self.children, it won't work with the Optional wrapper element, but I think this should work if we store panes in self.children and type-check through self.children instead. I get the idea though that we create separate render methods on children and rather than having separate add methods on parent.

On Wed, Feb 1, 2023, 4:52 PM Anand Chitipothu @.***> wrote:

Why not something like this?

class TabView: def init(self): self.panes = []

def render(self):
    a = self.render_tabs()
    b = self.render_content()
    return html.div(a, b).render()

def render_tabs(self):
    tabs = [pane.render_tab() for pane in self.panes]
    return html.ul(*tabs, class_="nav nav-tabs", role="tablist")

def render_content(self):
    contents = [pane.render_content() for pane in self.panes]
    return html.div(*contents, class_="nav-content")

class TabPane: def init(self, title, content): self.title = title self.content = content

...

— Reply to this email directly, view it on GitHub https://github.com/pipalacademy/kutty/issues/26#issuecomment-1411898906, or unsubscribe https://github.com/notifications/unsubscribe-auth/AI7MKYOM7JWWQNXWF53BI7TWVJBODANCNFSM6AAAAAAUNKGK2U . You are receiving this because you authored the thread.Message ID: @.***>

anandology commented 1 year ago

Why should Optional look at children. It should probably call an is_empty method with a default implementation using children . Subclasses should be able to provide custom implementation for that.

nikochiko commented 1 year ago

Agreed! This makes more sense.

On Wed, Feb 1, 2023, 5:40 PM Anand Chitipothu @.***> wrote:

Why should Optional look at children. It should probably call an is_empty method with a default implementation using children . Subclasses should be able to provide custom implementation for that.

— Reply to this email directly, view it on GitHub https://github.com/pipalacademy/kutty/issues/26#issuecomment-1411957512, or unsubscribe https://github.com/notifications/unsubscribe-auth/AI7MKYJPGCD2KVURUZ54JGLWVJHE3ANCNFSM6AAAAAAUNKGK2U . You are receiving this because you authored the thread.Message ID: @.***>