649 lines
23 KiB
Markdown
649 lines
23 KiB
Markdown
# TabsManager Component
|
||
|
||
## Introduction
|
||
|
||
The TabsManager component provides a dynamic tabbed interface for organizing multiple views within your FastHTML
|
||
application. It handles tab creation, activation, closing, and content management with automatic state persistence and
|
||
HTMX-powered interactions.
|
||
|
||
**Key features:**
|
||
|
||
- Dynamic tab creation and removal at runtime
|
||
- Automatic content caching for performance
|
||
- Session-based state persistence (tabs, order, active tab)
|
||
- Duplicate tab detection based on component identity
|
||
- Built-in search menu for quick tab navigation
|
||
- Auto-increment labels for programmatic tab creation
|
||
- HTMX-powered updates without page reload
|
||
|
||
**Common use cases:**
|
||
|
||
- Multi-document editor (code editor, text editor)
|
||
- Dashboard with multiple data views
|
||
- Settings interface with different configuration panels
|
||
- Developer tools with console, inspector, network tabs
|
||
- Application with dynamic content sections
|
||
|
||
## Quick Start
|
||
|
||
Here's a minimal example showing a tabbed interface with three views:
|
||
|
||
```python
|
||
from fasthtml.common import *
|
||
from myfasthtml.controls.TabsManager import TabsManager
|
||
from myfasthtml.core.instances import RootInstance
|
||
|
||
# Create root instance and tabs manager
|
||
root = RootInstance(session)
|
||
tabs = TabsManager(parent=root)
|
||
|
||
# Create three tabs with different content
|
||
tabs.create_tab("Dashboard", Div(H1("Dashboard"), P("Overview of your data")))
|
||
tabs.create_tab("Settings", Div(H1("Settings"), P("Configure your preferences")))
|
||
tabs.create_tab("Profile", Div(H1("Profile"), P("Manage your profile")))
|
||
|
||
# Render the tabs manager
|
||
return tabs
|
||
```
|
||
|
||
This creates a complete tabbed interface with:
|
||
|
||
- A header bar displaying three clickable tab buttons ("Dashboard", "Settings", "Profile")
|
||
- Close buttons (×) on each tab for dynamic removal
|
||
- A main content area showing the active tab's content
|
||
- A search menu (⊞ icon) for quick tab navigation when many tabs are open
|
||
- Automatic HTMX updates when switching or closing tabs
|
||
|
||
**Note:** Tabs are interactive by default. Users can click tab labels to switch views, click close buttons to remove
|
||
tabs, or use the search menu to find tabs quickly. All interactions update the UI without page reload thanks to HTMX
|
||
integration.
|
||
|
||
## Basic Usage
|
||
|
||
### Visual Structure
|
||
|
||
The TabsManager component consists of a header with tab buttons and a content area:
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────┐
|
||
│ Tab Header │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────┐ │
|
||
│ │ Tab 1 × │ │ Tab 2 × │ │ Tab 3 × │ │ ⊞ │ │
|
||
│ └──────────┘ └──────────┘ └──────────┘ └────┘ │
|
||
├────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ │
|
||
│ Active Tab Content │
|
||
│ │
|
||
│ │
|
||
└────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Component details:**
|
||
|
||
| Element | Description |
|
||
|------------------|-----------------------------------------|
|
||
| Tab buttons | Clickable labels to switch between tabs |
|
||
| Close button (×) | Removes the tab and its content |
|
||
| Search menu (⊞) | Dropdown menu to search and filter tabs |
|
||
| Content area | Displays the active tab's content |
|
||
|
||
### Creating a TabsManager
|
||
|
||
The TabsManager is a `MultipleInstance`, meaning you can create multiple independent tab managers in your application.
|
||
Create it by providing a parent instance:
|
||
|
||
```python
|
||
tabs = TabsManager(parent=root_instance)
|
||
|
||
# Or with a custom ID
|
||
tabs = TabsManager(parent=root_instance, _id="my-tabs")
|
||
```
|
||
|
||
### Creating Tabs
|
||
|
||
Use the `create_tab()` method to add a new tab:
|
||
|
||
```python
|
||
# Create a tab with custom content
|
||
tab_id = tabs.create_tab(
|
||
label="My Tab",
|
||
component=Div(H1("Content"), P("Tab content here"))
|
||
)
|
||
|
||
# Create with a MyFastHtml control
|
||
from myfasthtml.controls.VisNetwork import VisNetwork
|
||
|
||
network = VisNetwork(parent=tabs, nodes=nodes_data, edges=edges_data)
|
||
tab_id = tabs.create_tab("Network View", network)
|
||
|
||
# Create without activating immediately
|
||
tab_id = tabs.create_tab("Background Tab", content, activate=False)
|
||
```
|
||
|
||
**Parameters:**
|
||
|
||
- `label` (str): Display text shown in the tab button
|
||
- `component` (Any): Content to display in the tab (FastHTML elements or MyFastHtml controls)
|
||
- `activate` (bool): Whether to make this tab active immediately (default: True)
|
||
|
||
**Returns:** A unique `tab_id` (UUID string) that identifies the tab
|
||
|
||
### Showing Tabs
|
||
|
||
Use the `show_tab()` method to activate and display a tab:
|
||
|
||
```python
|
||
# Show a tab (makes it active and sends content to client if needed)
|
||
tabs.show_tab(tab_id)
|
||
|
||
# Show without activating (just send content to client)
|
||
tabs.show_tab(tab_id, activate=False)
|
||
```
|
||
|
||
**Parameters:**
|
||
|
||
- `tab_id` (str): The UUID of the tab to show
|
||
- `activate` (bool): Whether to make this tab active (default: True)
|
||
|
||
**Note:** The first time a tab is shown, its content is sent to the client and cached. Subsequent activations just
|
||
toggle visibility without re-sending content.
|
||
|
||
### Closing Tabs
|
||
|
||
Use the `close_tab()` method to remove a tab:
|
||
|
||
```python
|
||
# Close a specific tab
|
||
tabs.close_tab(tab_id)
|
||
```
|
||
|
||
**What happens when closing:**
|
||
|
||
1. Tab is removed from the tab list and order
|
||
2. Content is removed from cache and client
|
||
3. If the closed tab was active, the first remaining tab becomes active
|
||
4. If no tabs remain, `active_tab` is set to `None`
|
||
|
||
### Changing Tab Content
|
||
|
||
Use the `change_tab_content()` method to update an existing tab's content and label:
|
||
|
||
```python
|
||
# Update tab content and label
|
||
new_content = Div(H1("Updated"), P("New content"))
|
||
tabs.change_tab_content(
|
||
tab_id=tab_id,
|
||
label="Updated Tab",
|
||
component=new_content,
|
||
activate=True
|
||
)
|
||
```
|
||
|
||
**Parameters:**
|
||
|
||
- `tab_id` (str): The UUID of the tab to update
|
||
- `label` (str): New label for the tab
|
||
- `component` (Any): New content to display
|
||
- `activate` (bool): Whether to activate the tab after updating (default: True)
|
||
|
||
**Note:** This method forces the new content to be sent to the client, even if the tab was already displayed.
|
||
|
||
## Advanced Features
|
||
|
||
### Auto-increment Labels
|
||
|
||
When creating multiple tabs programmatically, you can use auto-increment to generate unique labels:
|
||
|
||
```python
|
||
# Using the on_new_tab method with auto_increment
|
||
def create_multiple_tabs():
|
||
# Creates "Untitled_0", "Untitled_1", "Untitled_2"
|
||
tabs.on_new_tab("Untitled", content, auto_increment=True)
|
||
tabs.on_new_tab("Untitled", content, auto_increment=True)
|
||
tabs.on_new_tab("Untitled", content, auto_increment=True)
|
||
```
|
||
|
||
**How it works:**
|
||
|
||
- The TabsManager maintains an internal counter (`_tab_count`)
|
||
- When `auto_increment=True`, the counter value is appended to the label
|
||
- Counter increments with each auto-incremented tab creation
|
||
- Useful for "New Tab 1", "New Tab 2" patterns in editors or tools
|
||
|
||
### Duplicate Detection
|
||
|
||
The TabsManager automatically detects and reuses tabs with identical content to prevent duplicates:
|
||
|
||
```python
|
||
# Create a control instance
|
||
network = VisNetwork(parent=tabs, nodes=data, edges=edges)
|
||
|
||
# First call creates a new tab
|
||
tab_id_1 = tabs.create_tab("Network", network)
|
||
|
||
# Second call with same label and component returns existing tab_id
|
||
tab_id_2 = tabs.create_tab("Network", network)
|
||
|
||
# tab_id_1 == tab_id_2 (True - same tab!)
|
||
```
|
||
|
||
**Detection criteria:**
|
||
A tab is considered a duplicate if all three match:
|
||
|
||
- Same `label`
|
||
- Same `component_type` (component class prefix)
|
||
- Same `component_id` (component instance ID)
|
||
|
||
**Note:** This only works with `BaseInstance` components (MyFastHtml controls). Plain FastHTML elements don't have IDs
|
||
and will always create new tabs.
|
||
|
||
### Dynamic Content Updates
|
||
|
||
You can update tabs dynamically during the session:
|
||
|
||
```python
|
||
# Initial tab creation
|
||
tab_id = tabs.create_tab("Data View", Div("Loading..."))
|
||
|
||
|
||
# Later, update with actual data
|
||
def load_data():
|
||
data_content = Div(H2("Data"), P("Loaded content"))
|
||
tabs.change_tab_content(tab_id, "Data View", data_content)
|
||
# Returns HTMX response to update the UI
|
||
```
|
||
|
||
**Use cases:**
|
||
|
||
- Loading data asynchronously
|
||
- Refreshing tab content based on user actions
|
||
- Updating visualizations with new data
|
||
- Switching between different views in the same tab
|
||
|
||
### Tab Search Menu
|
||
|
||
The built-in search menu helps users navigate when many tabs are open:
|
||
|
||
```python
|
||
# The search menu is automatically created and includes:
|
||
# - A Search control for filtering tabs by label
|
||
# - Live filtering as you type
|
||
# - Click to activate a tab from search results
|
||
```
|
||
|
||
**How to access:**
|
||
|
||
- Click the ⊞ icon in the tab header
|
||
- Start typing to filter tabs by label
|
||
- Click a result to activate that tab
|
||
|
||
The search menu updates automatically when tabs are added or removed.
|
||
|
||
### HTMX Out-of-Band Swaps
|
||
|
||
For advanced HTMX control, you can customize swap behavior:
|
||
|
||
```python
|
||
# Standard behavior (out-of-band swap enabled)
|
||
tabs.show_tab(tab_id, oob=True) # Default
|
||
|
||
# Custom target behavior (disable out-of-band)
|
||
tabs.show_tab(tab_id, oob=False) # Swap into HTMX target only
|
||
```
|
||
|
||
**When to use `oob=False`:**
|
||
|
||
- When you want to control the exact HTMX target
|
||
- When combining with other HTMX responses
|
||
- When the tab activation is triggered by a command with a specific target
|
||
|
||
**When to use `oob=True` (default):**
|
||
|
||
- Most common use case
|
||
- Allows other controls to trigger tab changes without caring about targets
|
||
- Enables automatic UI updates across multiple elements
|
||
|
||
### CSS Customization
|
||
|
||
The TabsManager uses CSS classes that you can customize:
|
||
|
||
| Class | Element |
|
||
|--------------------------|---------------------------------|
|
||
| `mf-tabs-manager` | Root tabs manager container |
|
||
| `mf-tabs-header-wrapper` | Header wrapper (buttons + menu) |
|
||
| `mf-tabs-header` | Tab buttons container |
|
||
| `mf-tab-button` | Individual tab button |
|
||
| `mf-tab-active` | Active tab button (modifier) |
|
||
| `mf-tab-label` | Tab label text |
|
||
| `mf-tab-close-btn` | Close button (×) |
|
||
| `mf-tab-content-wrapper` | Content area container |
|
||
| `mf-tab-content` | Individual tab content |
|
||
| `mf-empty-content` | Empty state when no tabs |
|
||
|
||
**Example customization:**
|
||
|
||
```css
|
||
/* Change active tab color */
|
||
.mf-tab-active {
|
||
background-color: #3b82f6;
|
||
color: white;
|
||
}
|
||
|
||
/* Customize close button */
|
||
.mf-tab-close-btn:hover {
|
||
color: red;
|
||
}
|
||
|
||
/* Style the content area */
|
||
.mf-tab-content-wrapper {
|
||
padding: 2rem;
|
||
background-color: #f9fafb;
|
||
}
|
||
```
|
||
|
||
## Examples
|
||
|
||
### Example 1: Multi-view Application
|
||
|
||
A typical application with different views accessible through tabs:
|
||
|
||
```python
|
||
from fasthtml.common import *
|
||
from myfasthtml.controls.TabsManager import TabsManager
|
||
from myfasthtml.core.instances import RootInstance
|
||
|
||
# Create tabs manager
|
||
root = RootInstance(session)
|
||
tabs = TabsManager(parent=root, _id="app-tabs")
|
||
|
||
# Dashboard view
|
||
dashboard = Div(
|
||
H1("Dashboard"),
|
||
Div(
|
||
Div("Total Users: 1,234", cls="stat"),
|
||
Div("Active Sessions: 56", cls="stat"),
|
||
Div("Revenue: $12,345", cls="stat"),
|
||
cls="stats-grid"
|
||
)
|
||
)
|
||
|
||
# Analytics view
|
||
analytics = Div(
|
||
H1("Analytics"),
|
||
P("Detailed analytics and reports"),
|
||
Div("Chart placeholder", cls="chart-container")
|
||
)
|
||
|
||
# Settings view
|
||
settings = Div(
|
||
H1("Settings"),
|
||
Form(
|
||
Label("Username:", Input(name="username", value="admin")),
|
||
Label("Email:", Input(name="email", value="admin@example.com")),
|
||
Button("Save", type="submit"),
|
||
)
|
||
)
|
||
|
||
# Create tabs
|
||
tabs.create_tab("Dashboard", dashboard)
|
||
tabs.create_tab("Analytics", analytics)
|
||
tabs.create_tab("Settings", settings)
|
||
|
||
# Render
|
||
return tabs
|
||
```
|
||
|
||
### Example 2: Dynamic Tabs with VisNetwork
|
||
|
||
Creating tabs dynamically with interactive network visualizations:
|
||
|
||
```python
|
||
from fasthtml.common import *
|
||
from myfasthtml.controls.TabsManager import TabsManager
|
||
from myfasthtml.controls.VisNetwork import VisNetwork
|
||
from myfasthtml.controls.helpers import mk
|
||
from myfasthtml.core.commands import Command
|
||
from myfasthtml.core.instances import RootInstance
|
||
|
||
root = RootInstance(session)
|
||
tabs = TabsManager(parent=root, _id="network-tabs")
|
||
|
||
# Create initial tab with welcome message
|
||
tabs.create_tab("Welcome", Div(
|
||
H1("Network Visualizer"),
|
||
P("Click 'Add Network' to create a new network visualization")
|
||
))
|
||
|
||
|
||
# Function to create a new network tab
|
||
def add_network_tab():
|
||
# Define network data
|
||
nodes = [
|
||
{"id": 1, "label": "Node 1"},
|
||
{"id": 2, "label": "Node 2"},
|
||
{"id": 3, "label": "Node 3"}
|
||
]
|
||
edges = [
|
||
{"from": 1, "to": 2},
|
||
{"from": 2, "to": 3}
|
||
]
|
||
|
||
# Create network instance
|
||
network = VisNetwork(parent=tabs, nodes=nodes, edges=edges)
|
||
|
||
# Use auto-increment to create unique labels
|
||
return tabs.on_new_tab("Network", network, auto_increment=True)
|
||
|
||
|
||
# Create command for adding networks
|
||
add_cmd = Command("add_network", "Add network tab", add_network_tab)
|
||
|
||
# Add button to create new network tabs
|
||
add_button = mk.button("Add Network", command=add_cmd, cls="btn btn-primary")
|
||
|
||
# Return tabs and button
|
||
return Div(add_button, tabs)
|
||
```
|
||
|
||
### Example 3: Tab Management with Content Updates
|
||
|
||
An application that updates tab content based on user interaction:
|
||
|
||
```python
|
||
from fasthtml.common import *
|
||
from myfasthtml.controls.TabsManager import TabsManager
|
||
from myfasthtml.controls.helpers import mk
|
||
from myfasthtml.core.commands import Command
|
||
from myfasthtml.core.instances import RootInstance
|
||
|
||
root = RootInstance(session)
|
||
tabs = TabsManager(parent=root, _id="editor-tabs")
|
||
|
||
# Create initial document tabs
|
||
doc1_id = tabs.create_tab("Document 1", Textarea("Initial content 1", rows=10))
|
||
doc2_id = tabs.create_tab("Document 2", Textarea("Initial content 2", rows=10))
|
||
|
||
|
||
# Function to refresh a document's content
|
||
def refresh_document(tab_id, doc_name):
|
||
# Simulate loading new content
|
||
new_content = Textarea(f"Refreshed content for {doc_name}\nTimestamp: {datetime.now()}", rows=10)
|
||
tabs.change_tab_content(tab_id, doc_name, new_content)
|
||
return tabs._mk_tabs_controller(oob=True), tabs._mk_tabs_header_wrapper(oob=True)
|
||
|
||
|
||
# Create refresh commands
|
||
refresh_doc1 = Command("refresh_1", "Refresh doc 1", refresh_document, doc1_id, "Document 1")
|
||
refresh_doc2 = Command("refresh_2", "Refresh doc 2", refresh_document, doc2_id, "Document 2")
|
||
|
||
# Add refresh buttons
|
||
controls = Div(
|
||
mk.button("Refresh Document 1", command=refresh_doc1, cls="btn btn-sm"),
|
||
mk.button("Refresh Document 2", command=refresh_doc2, cls="btn btn-sm"),
|
||
cls="controls-bar"
|
||
)
|
||
|
||
return Div(controls, tabs)
|
||
```
|
||
|
||
### Example 4: Using Auto-increment for Dynamic Tabs
|
||
|
||
Creating multiple tabs programmatically with auto-generated labels:
|
||
|
||
```python
|
||
from fasthtml.common import *
|
||
from myfasthtml.controls.TabsManager import TabsManager
|
||
from myfasthtml.controls.helpers import mk
|
||
from myfasthtml.core.commands import Command
|
||
from myfasthtml.core.instances import RootInstance
|
||
|
||
root = RootInstance(session)
|
||
tabs = TabsManager(parent=root, _id="dynamic-tabs")
|
||
|
||
# Create initial placeholder tab
|
||
tabs.create_tab("Start", Div(
|
||
H2("Welcome"),
|
||
P("Click 'New Tab' to create numbered tabs")
|
||
))
|
||
|
||
|
||
# Function to create a new numbered tab
|
||
def create_numbered_tab():
|
||
content = Div(
|
||
H2("New Tab Content"),
|
||
P(f"This tab was created dynamically"),
|
||
Input(placeholder="Enter some text...", cls="input")
|
||
)
|
||
# Auto-increment creates "Tab_0", "Tab_1", "Tab_2", etc.
|
||
return tabs.on_new_tab("Tab", content, auto_increment=True)
|
||
|
||
|
||
# Create command
|
||
new_tab_cmd = Command("new_tab", "Create new tab", create_numbered_tab)
|
||
|
||
# Add button
|
||
new_tab_button = mk.button("New Tab", command=new_tab_cmd, cls="btn btn-primary")
|
||
|
||
return Div(
|
||
Div(new_tab_button, cls="toolbar"),
|
||
tabs
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## Developer Reference
|
||
|
||
This section contains technical details for developers working on the TabsManager component itself.
|
||
|
||
### State
|
||
|
||
The TabsManager component maintains the following state properties:
|
||
|
||
| Name | Type | Description | Default |
|
||
|--------------------------|----------------|---------------------------------------------------|---------|
|
||
| `tabs` | dict[str, Any] | Dictionary of tab metadata (id, label, component) | `{}` |
|
||
| `tabs_order` | list[str] | Ordered list of tab IDs | `[]` |
|
||
| `active_tab` | str \| None | ID of the currently active tab | `None` |
|
||
| `ns_tabs_content` | dict[str, Any] | Cache of tab content (raw, not wrapped) | `{}` |
|
||
| `ns_tabs_sent_to_client` | set | Set of tab IDs already sent to client | `set()` |
|
||
|
||
**Note:** Properties prefixed with `ns_` are not persisted in the database and exist only for the session.
|
||
|
||
### Commands
|
||
|
||
Available commands for programmatic control:
|
||
|
||
| Name | Description |
|
||
|---------------------------------------------|--------------------------------------------|
|
||
| `show_tab(tab_id)` | Activate or show a specific tab |
|
||
| `close_tab(tab_id)` | Close a specific tab |
|
||
| `add_tab(label, component, auto_increment)` | Add a new tab with optional auto-increment |
|
||
|
||
### Public Methods
|
||
|
||
| Method | Description |
|
||
|---------------------------------------------------------------|-------------------------------------------------|
|
||
| `create_tab(label, component, activate=True)` | Create a new tab or reuse existing duplicate |
|
||
| `show_tab(tab_id, activate=True, oob=True)` | Send tab to client and/or activate it |
|
||
| `close_tab(tab_id)` | Close and remove a tab |
|
||
| `change_tab_content(tab_id, label, component, activate=True)` | Update existing tab's label and content |
|
||
| `on_new_tab(label, component, auto_increment=False)` | Create and show tab with auto-increment support |
|
||
| `add_tab_btn()` | Returns add tab button element |
|
||
| `get_state()` | Returns the TabsManagerState object |
|
||
| `render()` | Renders the complete TabsManager component |
|
||
|
||
### High Level Hierarchical Structure
|
||
|
||
```
|
||
Div(id="{id}", cls="mf-tabs-manager")
|
||
├── Div(id="{id}-controller") # Controller (hidden, manages active state)
|
||
├── Div(id="{id}-header-wrapper") # Header wrapper
|
||
│ ├── Div(id="{id}-header") # Tab buttons container
|
||
│ │ ├── Div (mf-tab-button) # Tab button 1
|
||
│ │ │ ├── Span (mf-tab-label) # Label (clickable)
|
||
│ │ │ └── Span (mf-tab-close-btn) # Close button
|
||
│ │ ├── Div (mf-tab-button) # Tab button 2
|
||
│ │ └── ...
|
||
│ └── Div (dropdown) # Search menu
|
||
│ ├── Icon (tabs24_regular) # Menu toggle button
|
||
│ └── Div (dropdown-content) # Search component
|
||
├── Div(id="{id}-content-wrapper") # Content wrapper
|
||
│ ├── Div(id="{id}-{tab_id_1}-content") # Tab 1 content
|
||
│ ├── Div(id="{id}-{tab_id_2}-content") # Tab 2 content
|
||
│ └── ...
|
||
└── Script # Initialization script
|
||
```
|
||
|
||
### Element IDs
|
||
|
||
| Name | Description |
|
||
|-------------------------|-----------------------------------------|
|
||
| `{id}` | Root tabs manager container |
|
||
| `{id}-controller` | Hidden controller managing active state |
|
||
| `{id}-header-wrapper` | Header wrapper (buttons + search) |
|
||
| `{id}-header` | Tab buttons container |
|
||
| `{id}-content-wrapper` | Content area wrapper |
|
||
| `{id}-{tab_id}-content` | Individual tab content |
|
||
| `{id}-search` | Search component ID |
|
||
|
||
**Note:** `{id}` is the TabsManager instance ID, `{tab_id}` is the UUID of each tab.
|
||
|
||
### Internal Methods
|
||
|
||
These methods are used internally for rendering:
|
||
|
||
| Method | Description |
|
||
|-----------------------------------------|-----------------------------------------------------|
|
||
| `_mk_tabs_controller(oob=False)` | Renders the hidden controller element |
|
||
| `_mk_tabs_header_wrapper(oob=False)` | Renders the header wrapper with buttons and search |
|
||
| `_mk_tab_button(tab_data)` | Renders a single tab button |
|
||
| `_mk_tab_content_wrapper()` | Renders the content wrapper with active tab content |
|
||
| `_mk_tab_content(tab_id, content)` | Renders individual tab content div |
|
||
| `_mk_show_tabs_menu()` | Renders the search dropdown menu |
|
||
| `_wrap_tab_content(tab_content)` | Wraps tab content for HTMX out-of-band insertion |
|
||
| `_get_or_create_tab_content(tab_id)` | Gets tab content from cache or creates it |
|
||
| `_dynamic_get_content(tab_id)` | Retrieves component from InstancesManager |
|
||
| `_tab_already_exists(label, component)` | Checks if duplicate tab exists |
|
||
| `_add_or_update_tab(...)` | Internal method to add/update tab in state |
|
||
| `_get_ordered_tabs()` | Returns tabs ordered by tabs_order list |
|
||
| `_get_tab_list()` | Returns list of tab dictionaries in order |
|
||
| `_get_tab_count()` | Returns and increments internal tab counter |
|
||
|
||
### Tab Metadata Structure
|
||
|
||
Each tab in the `tabs` dictionary has the following structure:
|
||
|
||
```python
|
||
{
|
||
'id': 'uuid-string', # Unique tab identifier
|
||
'label': 'Tab Label', # Display label
|
||
'component_type': 'prefix', # Component class prefix (or None)
|
||
'component_id': 'instance-id' # Component instance ID (or None)
|
||
}
|
||
```
|
||
|
||
**Note:** `component_type` and `component_id` are `None` for plain FastHTML elements that don't inherit from
|
||
`BaseInstance`.
|