Skip to content
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

Add a basic tab group demo #1197

Merged
merged 1 commit into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions demo/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import slide_toggle as slide_toggle
import slider as slider
import snackbar as snackbar
import tab_group as tab_group
import table as table
import tailwind as tailwind
import text as text
Expand Down Expand Up @@ -112,6 +113,7 @@ class Section:
Example(name="grid_table"),
Example(name="headers"),
Example(name="snackbar"),
Example(name="tab_group"),
Example(name="chat_inputs"),
Example(name="form_billing"),
Example(name="form_profile"),
Expand Down
166 changes: 166 additions & 0 deletions demo/tab_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
"""Simple tab group example.

- Use this code as a starting point for creating tab group-like functionlity.
- Extend/Modify it for your specific use case.
"""

from dataclasses import dataclass, field
from typing import Callable

import mesop as me


@dataclass
class Tab:
label: str
content: Callable
selected: bool = False
disabled: bool = False
icon: str | None = None


def load(e: me.LoadEvent):
me.set_theme_mode("system")


@me.stateclass
class State:
selected_tab_index: int = 0
disabled_tab_indexes: set[int] = field(default_factory=lambda: {1})


@me.page(
on_load=load,
security_policy=me.SecurityPolicy(
allowed_iframe_parents=["https://google.github.io"]
),
path="/tab_group",
)
def app():
with me.box():
tab_group(make_tabs(), on_tab_click)


def make_tabs() -> list[Tab]:
"""Helper to make the tabs for the tab group demo."""
state = me.state(State)
tabs = [
Tab(label="Tab 1", icon="home", content=tab_1),
Tab(label="Tab 2", content=tab_2),
Tab(label="Tab 3", icon="tab", content=tab_3),
]
for index, tab in enumerate(tabs):
tab.selected = state.selected_tab_index == index
tab.disabled = index in state.disabled_tab_indexes
return tabs


@me.component
def tab_1():
me.text("Tab 1")


@me.component
def tab_2():
me.text("Tab 2")


@me.component
def tab_3():
me.text("Tab 3")


def on_tab_click(e: me.ClickEvent):
"""Click handler that handles updating the tabs when clicked."""
state = me.state(State)

_, tab_index = e.key.split("-")
tab_index = int(tab_index)

if tab_index == state.selected_tab_index:
return
if tab_index in state.disabled_tab_indexes:
return

state.selected_tab_index = int(tab_index)


@me.component
def tab_group(tabs: list[Tab], on_tab_click: Callable):
"""Generates the tab group component

Args:
tabs: Metadata for rendering tabs for the tab group
on_tab_click: Event to handle what happens when a tab header is clicked
"""
tab_header(tabs, on_tab_click)
tab_content(tabs)


@me.component
def tab_header(tabs: list[Tab], on_tab_click: Callable):
"""Generates the header for the tab group."""
with me.box(
style=me.Style(
display="flex",
width="100%",
border=me.Border(
bottom=me.BorderSide(
width=1, style="solid", color=me.theme_var("outline-variant")
)
),
)
):
for index, tab in enumerate(tabs):
with me.box(
key=f"tab-{index}",
on_click=on_tab_click,
style=make_tab_style(tab.selected, tab.disabled),
):
if tab.icon:
me.icon(tab.icon)
me.text(tab.label)


@me.component
def tab_content(tabs: list[Tab]):
"""Component for rendering the content of the selected tab."""
for tab in tabs:
if tab.selected:
with me.box():
tab.content()


def make_tab_style(selected: bool, disabled: bool) -> me.Style:
"""Makes the styles for the tab based on selected/disabled state."""
style = make_default_tab_style()
if disabled:
style.color = me.theme_var("outline")
style.cursor = "default"
elif selected:
style.background = me.theme_var("surface-container")
style.border = me.Border(
bottom=me.BorderSide(
width=2, style="solid", color=me.theme_var("primary")
)
)
style.cursor = "default"
return style


def make_default_tab_style():
"""Basic styles shared by different tab state (selected, disabled, default)."""
return me.Style(
align_items="center",
color=me.theme_var("on-surface"),
display="flex",
cursor="pointer",
flex_grow=1,
justify_content="center",
line_height=1,
font_size=14,
font_weight="medium",
padding=me.Padding.all(16),
text_align="center",
gap=5,
)
Loading