833 lines
25 KiB
Markdown
833 lines
25 KiB
Markdown
# Panel Component
|
||
|
||
## Introduction
|
||
|
||
The Panel component provides a flexible three-zone layout with optional collapsible side panels. It's designed to
|
||
organize content into left panel, main area, and right panel sections, with smooth toggle animations and resizable
|
||
panels.
|
||
|
||
**Key features:**
|
||
|
||
- Three customizable zones (left panel, main content, right panel)
|
||
- Toggle visibility with hide/show icons
|
||
- Resizable panels with drag handles
|
||
- Smooth CSS animations for show/hide transitions
|
||
- Automatic state persistence per session
|
||
- Configurable panel presence (enable/disable left or right)
|
||
- Session-based width and visibility state
|
||
|
||
**Common use cases:**
|
||
|
||
- Code editor with file explorer and properties panel
|
||
- Data visualization with filters sidebar and details panel
|
||
- Admin interface with navigation menu and tools panel
|
||
- Documentation viewer with table of contents and metadata
|
||
- Dashboard with configuration panel and information sidebar
|
||
|
||
## Quick Start
|
||
|
||
Here's a minimal example showing a three-panel layout for a code editor:
|
||
|
||
```python
|
||
from fasthtml.common import *
|
||
from myfasthtml.controls.Panel import Panel
|
||
|
||
# Create the panel instance
|
||
panel = Panel(parent=root_instance)
|
||
|
||
# Set content for each zone
|
||
panel.set_left(
|
||
Div(
|
||
H3("Files"),
|
||
Ul(
|
||
Li("app.py"),
|
||
Li("config.py"),
|
||
Li("utils.py")
|
||
)
|
||
)
|
||
)
|
||
|
||
panel.set_main(
|
||
Div(
|
||
H2("Editor"),
|
||
Textarea("# Write your code here", rows=20, cls="w-full font-mono")
|
||
)
|
||
)
|
||
|
||
panel.set_right(
|
||
Div(
|
||
H3("Properties"),
|
||
Div("Language: Python"),
|
||
Div("Lines: 120"),
|
||
Div("Size: 3.2 KB")
|
||
)
|
||
)
|
||
|
||
# Render the panel
|
||
return panel
|
||
```
|
||
|
||
This creates a complete panel layout with:
|
||
|
||
- A left panel displaying a file list with a hide icon (−) at the top right
|
||
- A main content area with a code editor
|
||
- A right panel showing file properties with a hide icon (−) at the top right
|
||
- Show icons (⋯) that appear in the main area when panels are hidden
|
||
- Drag handles between panels for manual resizing
|
||
- Automatic state persistence (visibility and width)
|
||
|
||
**Note:** Users can hide panels by clicking the hide icon (−) inside each panel. When hidden, a show icon (⋯) appears in
|
||
the main area (left side for left panel, right side for right panel). Panels can be resized by dragging the handles, and
|
||
all state is automatically saved in the session.
|
||
|
||
## Basic Usage
|
||
|
||
### Visual Structure
|
||
|
||
The Panel component consists of three zones with optional side panels:
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────┐
|
||
│ │
|
||
│ ┌──────────┐ │ ┌──────────────────────┐ │ ┌──────────┐ │
|
||
│ │ │ │ │ │ │ │ │ │
|
||
│ │ Left │ ║ │ │ ║ │ Right │ │
|
||
│ │ Panel │ │ │ Main Content │ │ │ Panel │ │
|
||
│ │ │ │ │ │ │ │ │ │
|
||
│ │ [−] │ │ │ [⋯] [⋯] │ │ │ [−] │ │
|
||
│ └──────────┘ │ └──────────────────────┘ │ └──────────┘ │
|
||
│ ║ ║ │
|
||
│ Resizer Resizer │
|
||
└────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Component details:**
|
||
|
||
| Element | Description |
|
||
|---------------|-----------------------------------------------|
|
||
| Left panel | Optional collapsible panel (default: visible) |
|
||
| Main content | Always-visible central content area |
|
||
| Right panel | Optional collapsible panel (default: visible) |
|
||
| Hide icon (−) | Inside each panel, top right corner |
|
||
| Show icon (⋯) | In main area when panel is hidden |
|
||
| Resizer (║) | Drag handle to resize panels manually |
|
||
|
||
### Creating a Panel
|
||
|
||
The Panel is a `MultipleInstance`, meaning you can create multiple independent panels in your application. Create it by
|
||
providing a parent instance:
|
||
|
||
```python
|
||
panel = Panel(parent=root_instance)
|
||
|
||
# Or with a custom ID
|
||
panel = Panel(parent=root_instance, _id="my-panel")
|
||
|
||
# Or with custom configuration
|
||
from myfasthtml.controls.Panel import PanelConf
|
||
|
||
conf = PanelConf(left=True, right=False) # Only left panel enabled
|
||
panel = Panel(parent=root_instance, conf=conf)
|
||
```
|
||
|
||
### Content Zones
|
||
|
||
The Panel provides three content zones:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ Left Panel │ Main Content │ Right Panel │
|
||
│ (optional) │ (required) │ (optional) │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Zone details:**
|
||
|
||
| Zone | Typical Use | Required |
|
||
|---------|-------------------------------------------------------|----------|
|
||
| `left` | Navigation, file explorer, filters, table of contents | No |
|
||
| `main` | Primary content, editor, visualization, results | Yes |
|
||
| `right` | Properties, tools, metadata, debug info, settings | No |
|
||
|
||
### Setting Content
|
||
|
||
Use the `set_*()` methods to add content to each zone:
|
||
|
||
```python
|
||
# Main content (always visible)
|
||
panel.set_main(
|
||
Div(
|
||
H1("Dashboard"),
|
||
P("This is the main content area")
|
||
)
|
||
)
|
||
|
||
# Left panel (optional)
|
||
panel.set_left(
|
||
Div(
|
||
H3("Navigation"),
|
||
Ul(
|
||
Li("Home"),
|
||
Li("Settings"),
|
||
Li("About")
|
||
)
|
||
)
|
||
)
|
||
|
||
# Right panel (optional)
|
||
panel.set_right(
|
||
Div(
|
||
H3("Tools"),
|
||
Button("Export"),
|
||
Button("Refresh")
|
||
)
|
||
)
|
||
```
|
||
|
||
**Method chaining:**
|
||
|
||
The `set_main()` method returns `self`, enabling method chaining:
|
||
|
||
```python
|
||
panel = Panel(parent=root_instance)
|
||
.set_main(Div("Main content"))
|
||
.set_left(Div("Left content"))
|
||
```
|
||
|
||
### Panel Configuration
|
||
|
||
By default, both left and right panels are enabled. You can customize this with `PanelConf`:
|
||
|
||
```python
|
||
from myfasthtml.controls.Panel import PanelConf
|
||
|
||
# Only left panel enabled
|
||
conf = PanelConf(left=True, right=False)
|
||
panel = Panel(parent=root_instance, conf=conf)
|
||
|
||
# Only right panel enabled
|
||
conf = PanelConf(left=False, right=True)
|
||
panel = Panel(parent=root_instance, conf=conf)
|
||
|
||
# Both panels enabled (default)
|
||
conf = PanelConf(left=True, right=True)
|
||
panel = Panel(parent=root_instance, conf=conf)
|
||
|
||
# No side panels (main content only)
|
||
conf = PanelConf(left=False, right=False)
|
||
panel = Panel(parent=root_instance, conf=conf)
|
||
```
|
||
|
||
**Note:** When a panel is disabled in configuration, it won't render at all. When a panel is hidden (via toggle), it
|
||
renders but with zero width and overflow hidden.
|
||
|
||
## Advanced Features
|
||
|
||
### Toggling Panel Visibility
|
||
|
||
Each visible panel includes a hide icon (−) in its top-right corner. When hidden, a show icon (⋯) appears in the main
|
||
area:
|
||
|
||
**User interaction:**
|
||
|
||
- **Hide panel**: Click the − icon inside the panel
|
||
- **Show panel**: Click the ⋯ icon in the main area
|
||
|
||
**Icon positions:**
|
||
|
||
- Hide icons (−): Always at top-right of each panel
|
||
- Show icon for left panel (⋯): Top-left of main area
|
||
- Show icon for right panel (⋯): Top-right of main area
|
||
|
||
**Visual states:**
|
||
|
||
```
|
||
Panel Visible:
|
||
┌──────────┐
|
||
│ Content │
|
||
│ [−] │ ← Hide icon visible
|
||
└──────────┘
|
||
|
||
Panel Hidden:
|
||
┌──────────────────┐
|
||
│ [⋯] Main │ ← Show icon visible in main
|
||
└──────────────────┘
|
||
```
|
||
|
||
**Animation:**
|
||
|
||
When toggling visibility:
|
||
|
||
- **Hiding**: Panel width animates to 0px over 0.3s
|
||
- **Showing**: Panel width animates to its saved width over 0.3s
|
||
- Content remains in DOM (state preserved)
|
||
- Smooth CSS transition with ease timing
|
||
|
||
**Note:** The animation only works when showing (panel appearing). When hiding, the transition currently doesn't apply
|
||
due to HTMX swap timing. This is a known limitation.
|
||
|
||
### Resizable Panels
|
||
|
||
Both left and right panels can be resized by users via drag handles:
|
||
|
||
- **Drag handle location**:
|
||
- Left panel: Right edge (vertical bar)
|
||
- Right panel: Left edge (vertical bar)
|
||
- **Width constraints**: 150px (minimum) to 500px (maximum)
|
||
- **Persistence**: Resized width is automatically saved in session state
|
||
- **No transition during resize**: CSS transitions are disabled during manual dragging for smooth performance
|
||
|
||
**How to resize:**
|
||
|
||
1. Hover over the panel edge (cursor changes to resize cursor)
|
||
2. Click and drag left/right
|
||
3. Release to set the new width
|
||
4. Width is saved automatically and persists in the session
|
||
|
||
**Initial widths:**
|
||
|
||
- Left panel: 250px
|
||
- Right panel: 250px
|
||
|
||
These defaults can be customized via state after creation if needed.
|
||
|
||
### State Persistence
|
||
|
||
The Panel automatically persists its state within the user's session:
|
||
|
||
| State Property | Description | Default |
|
||
|-----------------|--------------------------------|---------|
|
||
| `left_visible` | Whether left panel is visible | `True` |
|
||
| `right_visible` | Whether right panel is visible | `True` |
|
||
| `left_width` | Left panel width in pixels | `250` |
|
||
| `right_width` | Right panel width in pixels | `250` |
|
||
|
||
State changes (toggle visibility, resize width) are automatically saved and restored within the session.
|
||
|
||
**Accessing state:**
|
||
|
||
```python
|
||
# Check current state
|
||
is_left_visible = panel._state.left_visible
|
||
left_panel_width = panel._state.left_width
|
||
|
||
# Programmatically update state (not recommended - use commands instead)
|
||
panel._state.left_visible = False # Better to use toggle_side command
|
||
```
|
||
|
||
### Programmatic Control
|
||
|
||
You can control panels programmatically using commands:
|
||
|
||
```python
|
||
# Toggle panel visibility
|
||
toggle_left = panel.commands.toggle_side("left", visible=False) # Hide left
|
||
toggle_right = panel.commands.toggle_side("right", visible=True) # Show right
|
||
|
||
# Update panel width
|
||
update_left_width = panel.commands.update_side_width("left")
|
||
update_right_width = panel.commands.update_side_width("right")
|
||
```
|
||
|
||
These commands are typically used with buttons or other interactive elements:
|
||
|
||
```python
|
||
from myfasthtml.controls.helpers import mk
|
||
|
||
# Add buttons to toggle panels
|
||
hide_left_btn = mk.button("Hide Left", command=panel.commands.toggle_side("left", False))
|
||
show_left_btn = mk.button("Show Left", command=panel.commands.toggle_side("left", True))
|
||
|
||
# Add to your layout
|
||
panel.set_main(
|
||
Div(
|
||
hide_left_btn,
|
||
show_left_btn,
|
||
H1("Main Content")
|
||
)
|
||
)
|
||
```
|
||
|
||
**Command details:**
|
||
|
||
- `toggle_side(side, visible)`: Sets panel visibility explicitly
|
||
- `side`: `"left"` or `"right"`
|
||
- `visible`: `True` (show) or `False` (hide)
|
||
- Returns: tuple of (panel_element, show_icon_element) for HTMX swap
|
||
|
||
- `update_side_width(side)`: Updates panel width from HTMX request
|
||
- `side`: `"left"` or `"right"`
|
||
- Width value comes from JavaScript resize handler
|
||
- Returns: updated panel element for HTMX swap
|
||
|
||
### CSS Customization
|
||
|
||
The Panel uses CSS classes that you can customize:
|
||
|
||
| Class | Element |
|
||
|----------------------------|------------------------------------------|
|
||
| `mf-panel` | Root panel container |
|
||
| `mf-panel-left` | Left panel container |
|
||
| `mf-panel-right` | Right panel container |
|
||
| `mf-panel-main` | Main content area |
|
||
| `mf-panel-hide-icon` | Hide icon (−) inside panels |
|
||
| `mf-panel-show-icon` | Show icon (⋯) in main area |
|
||
| `mf-panel-show-icon-left` | Show icon for left panel |
|
||
| `mf-panel-show-icon-right` | Show icon for right panel |
|
||
| `mf-resizer` | Resize handle base class |
|
||
| `mf-resizer-left` | Left panel resize handle |
|
||
| `mf-resizer-right` | Right panel resize handle |
|
||
| `mf-hidden` | Applied to hidden panels |
|
||
| `no-transition` | Disables transition during manual resize |
|
||
|
||
**Example customization:**
|
||
|
||
```css
|
||
/* Change panel background color */
|
||
.mf-panel-left,
|
||
.mf-panel-right {
|
||
background-color: #f9fafb;
|
||
}
|
||
|
||
/* Customize hide icon appearance */
|
||
.mf-panel-hide-icon:hover {
|
||
background-color: rgba(0, 0, 0, 0.1);
|
||
color: #ef4444;
|
||
}
|
||
|
||
/* Change transition timing */
|
||
.mf-panel-left,
|
||
.mf-panel-right {
|
||
transition: width 0.5s ease-in-out; /* Slower animation */
|
||
}
|
||
|
||
/* Style resizer handles */
|
||
.mf-resizer {
|
||
background-color: #e5e7eb;
|
||
}
|
||
|
||
.mf-resizer:hover {
|
||
background-color: #3b82f6;
|
||
}
|
||
```
|
||
|
||
## Examples
|
||
|
||
### Example 1: Code Editor Layout
|
||
|
||
A typical code editor with file explorer, editor, and properties panel:
|
||
|
||
```python
|
||
from fasthtml.common import *
|
||
from myfasthtml.controls.Panel import Panel
|
||
|
||
# Create panel
|
||
panel = Panel(parent=root_instance)
|
||
|
||
# Left panel: File Explorer
|
||
panel.set_left(
|
||
Div(
|
||
H3("Explorer", cls="font-bold mb-2"),
|
||
Div(
|
||
Div("📁 src", cls="font-mono cursor-pointer"),
|
||
Div(" 📄 app.py", cls="font-mono ml-4 cursor-pointer"),
|
||
Div(" 📄 config.py", cls="font-mono ml-4 cursor-pointer"),
|
||
Div("📁 tests", cls="font-mono cursor-pointer"),
|
||
Div(" 📄 test_app.py", cls="font-mono ml-4 cursor-pointer"),
|
||
cls="space-y-1"
|
||
),
|
||
cls="p-4"
|
||
)
|
||
)
|
||
|
||
# Main: Code Editor
|
||
panel.set_main(
|
||
Div(
|
||
Div(
|
||
Span("app.py", cls="font-bold"),
|
||
Span("Python", cls="text-sm opacity-60 ml-2"),
|
||
cls="border-b pb-2 mb-2"
|
||
),
|
||
Textarea(
|
||
"""def main():
|
||
print("Hello, World!")
|
||
|
||
if __name__ == "__main__":
|
||
main()""",
|
||
rows=20,
|
||
cls="w-full font-mono text-sm p-2 border rounded"
|
||
),
|
||
cls="p-4"
|
||
)
|
||
)
|
||
|
||
# Right panel: Properties and Tools
|
||
panel.set_right(
|
||
Div(
|
||
H3("Properties", cls="font-bold mb-2"),
|
||
Div("Language: Python", cls="text-sm mb-1"),
|
||
Div("Lines: 5", cls="text-sm mb-1"),
|
||
Div("Size: 87 bytes", cls="text-sm mb-4"),
|
||
|
||
H3("Tools", cls="font-bold mb-2 mt-4"),
|
||
Button("Run", cls="btn btn-sm btn-primary w-full mb-2"),
|
||
Button("Debug", cls="btn btn-sm w-full mb-2"),
|
||
Button("Format", cls="btn btn-sm w-full"),
|
||
cls="p-4"
|
||
)
|
||
)
|
||
|
||
return panel
|
||
```
|
||
|
||
### Example 2: Dashboard with Filters
|
||
|
||
A data dashboard with filters sidebar and details panel:
|
||
|
||
```python
|
||
from fasthtml.common import *
|
||
from myfasthtml.controls.Panel import Panel
|
||
|
||
# Create panel
|
||
panel = Panel(parent=root_instance)
|
||
|
||
# Left panel: Filters
|
||
panel.set_left(
|
||
Div(
|
||
H3("Filters", cls="font-bold mb-3"),
|
||
|
||
Div(
|
||
Label("Date Range", cls="label"),
|
||
Select(
|
||
Option("Last 7 days"),
|
||
Option("Last 30 days"),
|
||
Option("Last 90 days"),
|
||
cls="select select-bordered w-full"
|
||
),
|
||
cls="mb-3"
|
||
),
|
||
|
||
Div(
|
||
Label("Category", cls="label"),
|
||
Div(
|
||
Label(Input(type="checkbox", cls="checkbox"), " Sales", cls="label cursor-pointer"),
|
||
Label(Input(type="checkbox", cls="checkbox"), " Marketing", cls="label cursor-pointer"),
|
||
Label(Input(type="checkbox", cls="checkbox"), " Support", cls="label cursor-pointer"),
|
||
cls="space-y-2"
|
||
),
|
||
cls="mb-3"
|
||
),
|
||
|
||
Button("Apply Filters", cls="btn btn-primary w-full"),
|
||
cls="p-4"
|
||
)
|
||
)
|
||
|
||
# Main: Dashboard Charts
|
||
panel.set_main(
|
||
Div(
|
||
H1("Analytics Dashboard", cls="text-2xl font-bold mb-4"),
|
||
|
||
Div(
|
||
Div(
|
||
Div("Total Revenue", cls="stat-title"),
|
||
Div("$45,231", cls="stat-value"),
|
||
Div("+12% from last month", cls="stat-desc"),
|
||
cls="stat"
|
||
),
|
||
Div(
|
||
Div("Active Users", cls="stat-title"),
|
||
Div("2,345", cls="stat-value"),
|
||
Div("+8% from last month", cls="stat-desc"),
|
||
cls="stat"
|
||
),
|
||
cls="stats shadow mb-4"
|
||
),
|
||
|
||
Div("[Chart placeholder - Revenue over time]", cls="border rounded p-8 text-center"),
|
||
cls="p-4"
|
||
)
|
||
)
|
||
|
||
# Right panel: Details and Insights
|
||
panel.set_right(
|
||
Div(
|
||
H3("Key Insights", cls="font-bold mb-3"),
|
||
|
||
Div(
|
||
Div("🎯 Top Performing", cls="font-bold mb-1"),
|
||
Div("Product A: $12,450", cls="text-sm"),
|
||
Div("Product B: $8,920", cls="text-sm mb-3")
|
||
),
|
||
|
||
Div(
|
||
Div("📊 Trending Up", cls="font-bold mb-1"),
|
||
Div("Category: Electronics", cls="text-sm"),
|
||
Div("+23% this week", cls="text-sm mb-3")
|
||
),
|
||
|
||
Div(
|
||
Div("⚠️ Needs Attention", cls="font-bold mb-1"),
|
||
Div("Low stock: Item X", cls="text-sm"),
|
||
Div("Response time: +15%", cls="text-sm")
|
||
),
|
||
cls="p-4"
|
||
)
|
||
)
|
||
|
||
return panel
|
||
```
|
||
|
||
### Example 3: Simple Layout (Main Content Only)
|
||
|
||
A minimal panel with no side panels, focusing only on main content:
|
||
|
||
```python
|
||
from fasthtml.common import *
|
||
from myfasthtml.controls.Panel import Panel, PanelConf
|
||
|
||
# Create panel with both side panels disabled
|
||
conf = PanelConf(left=False, right=False)
|
||
panel = Panel(parent=root_instance, conf=conf)
|
||
|
||
# Only main content
|
||
panel.set_main(
|
||
Article(
|
||
H1("Welcome to My Blog", cls="text-3xl font-bold mb-4"),
|
||
P("This is a simple layout focusing entirely on the main content."),
|
||
P("No side panels distract from the reading experience."),
|
||
P("The content takes up the full width of the container."),
|
||
cls="prose max-w-none p-8"
|
||
)
|
||
)
|
||
|
||
return panel
|
||
```
|
||
|
||
### Example 4: Dynamic Panel Updates
|
||
|
||
Controlling panels programmatically based on user interaction:
|
||
|
||
```python
|
||
from fasthtml.common import *
|
||
from myfasthtml.controls.Panel import Panel
|
||
from myfasthtml.controls.helpers import mk
|
||
from myfasthtml.core.commands import Command
|
||
|
||
# Create panel
|
||
panel = Panel(parent=root_instance)
|
||
|
||
# Set up content
|
||
panel.set_left(
|
||
Div(
|
||
H3("Navigation"),
|
||
Ul(
|
||
Li("Dashboard"),
|
||
Li("Reports"),
|
||
Li("Settings")
|
||
)
|
||
)
|
||
)
|
||
|
||
panel.set_right(
|
||
Div(
|
||
H3("Debug Info"),
|
||
Div("Session ID: abc123"),
|
||
Div("User: Admin"),
|
||
Div("Timestamp: 2024-01-15")
|
||
)
|
||
)
|
||
|
||
# Create control buttons
|
||
toggle_left_btn = mk.button(
|
||
"Toggle Left Panel",
|
||
command=panel.commands.toggle_side("left", False),
|
||
cls="btn btn-sm"
|
||
)
|
||
|
||
toggle_right_btn = mk.button(
|
||
"Toggle Right Panel",
|
||
command=panel.commands.toggle_side("right", False),
|
||
cls="btn btn-sm"
|
||
)
|
||
|
||
show_all_btn = mk.button(
|
||
"Show All Panels",
|
||
command=Command(
|
||
"show_all",
|
||
"Show all panels",
|
||
lambda: (
|
||
panel.toggle_side("left", True),
|
||
panel.toggle_side("right", True)
|
||
)
|
||
),
|
||
cls="btn btn-sm btn-primary"
|
||
)
|
||
|
||
# Main content with controls
|
||
panel.set_main(
|
||
Div(
|
||
H1("Panel Controls Demo", cls="text-2xl font-bold mb-4"),
|
||
|
||
Div(
|
||
toggle_left_btn,
|
||
toggle_right_btn,
|
||
show_all_btn,
|
||
cls="space-x-2 mb-4"
|
||
),
|
||
|
||
P("Use the buttons above to toggle panels programmatically."),
|
||
P("You can also use the hide (−) and show (⋯) icons."),
|
||
|
||
cls="p-4"
|
||
)
|
||
)
|
||
|
||
return panel
|
||
```
|
||
|
||
---
|
||
|
||
## Developer Reference
|
||
|
||
This section contains technical details for developers working on the Panel component itself.
|
||
|
||
### Configuration
|
||
|
||
The Panel component uses `PanelConf` dataclass for configuration:
|
||
|
||
| Property | Type | Description | Default |
|
||
|----------|---------|----------------------------|---------|
|
||
| `left` | boolean | Enable/disable left panel | `True` |
|
||
| `right` | boolean | Enable/disable right panel | `True` |
|
||
|
||
### State
|
||
|
||
The Panel component maintains the following state properties via `PanelState`:
|
||
|
||
| Name | Type | Description | Default |
|
||
|-----------------|---------|------------------------------------|---------|
|
||
| `left_visible` | boolean | True if the left panel is visible | `True` |
|
||
| `right_visible` | boolean | True if the right panel is visible | `True` |
|
||
| `left_width` | integer | Width of the left panel in pixels | `250` |
|
||
| `right_width` | integer | Width of the right panel in pixels | `250` |
|
||
|
||
### Commands
|
||
|
||
Available commands for programmatic control:
|
||
|
||
| Name | Description |
|
||
|------------------------------|-------------------------------------------------------------------|
|
||
| `toggle_side(side, visible)` | Sets panel visibility (side: "left"/"right", visible: True/False) |
|
||
| `update_side_width(side)` | Updates panel width from HTMX request (side: "left"/"right") |
|
||
|
||
**Note:** The old `toggle_side(side)` command without the `visible` parameter is deprecated but still available in the
|
||
codebase.
|
||
|
||
### Public Methods
|
||
|
||
| Method | Description | Returns |
|
||
|----------------------|------------------------------|---------|
|
||
| `set_main(content)` | Sets the main content area | `self` |
|
||
| `set_left(content)` | Sets the left panel content | `Div` |
|
||
| `set_right(content)` | Sets the right panel content | `Div` |
|
||
| `render()` | Renders the complete panel | `Div` |
|
||
|
||
### High Level Hierarchical Structure
|
||
|
||
```
|
||
Div(id="{id}", cls="mf-panel")
|
||
├── Div(id="{id}_pl", cls="mf-panel-left [mf-hidden]")
|
||
│ ├── Div (hide icon)
|
||
│ ├── Div(id="{id}_cl")
|
||
│ │ └── [Left content]
|
||
│ └── Div (resizer-left)
|
||
├── Div(cls="mf-panel-main")
|
||
│ ├── Div(id="{id}_show_left", cls="hidden|mf-panel-show-icon-left")
|
||
│ ├── Div(id="{id}_m", cls="mf-panel-main")
|
||
│ │ └── [Main content]
|
||
│ └── Div(id="{id}_show_right", cls="hidden|mf-panel-show-icon-right")
|
||
├── Div(id="{id}_pr", cls="mf-panel-right [mf-hidden]")
|
||
│ ├── Div (resizer-right)
|
||
│ ├── Div (hide icon)
|
||
│ └── Div(id="{id}_cr")
|
||
│ └── [Right content]
|
||
└── Script # initResizer('{id}')
|
||
```
|
||
|
||
**Note:**
|
||
|
||
- Left panel: hide icon, then content, then resizer (resizer on right edge)
|
||
- Right panel: resizer, then hide icon, then content (resizer on left edge)
|
||
- Hide icons are positioned at panel root level (not inside content div)
|
||
- Main content has an outer wrapper and inner content div with ID
|
||
- `[mf-hidden]` class is conditionally applied when panel is hidden
|
||
|
||
### Element IDs
|
||
|
||
| Name | Description |
|
||
|------------------|-------------------------------------|
|
||
| `{id}` | Root panel container |
|
||
| `{id}_pl` | Left panel container |
|
||
| `{id}_pr` | Right panel container |
|
||
| `{id}_cl` | Left panel content wrapper |
|
||
| `{id}_cr` | Right panel content wrapper |
|
||
| `{id}_m` | Main content wrapper |
|
||
| `{id}_show_left` | Show icon for left panel (in main) |
|
||
| `{id}_show_right`| Show icon for right panel (in main) |
|
||
|
||
**Note:** `{id}` is the Panel instance ID (auto-generated UUID or custom `_id`).
|
||
|
||
**ID Management:**
|
||
|
||
The Panel component uses the `PanelIds` helper class to manage element IDs consistently. Access IDs programmatically:
|
||
|
||
```python
|
||
panel = Panel(parent=root_instance)
|
||
|
||
# Access IDs via get_ids()
|
||
panel.get_ids().panel("left") # Returns "{id}_pl"
|
||
panel.get_ids().panel("right") # Returns "{id}_pr"
|
||
panel.get_ids().left # Returns "{id}_cl"
|
||
panel.get_ids().right # Returns "{id}_cr"
|
||
panel.get_ids().main # Returns "{id}_m"
|
||
panel.get_ids().content("left") # Returns "{id}_cl"
|
||
```
|
||
|
||
### Internal Methods
|
||
|
||
These methods are used internally for rendering:
|
||
|
||
| Method | Description |
|
||
|-----------------------|---------------------------------------------------|
|
||
| `_mk_panel(side)` | Renders a panel (left or right) with all elements |
|
||
| `_mk_show_icon(side)` | Renders the show icon for a panel |
|
||
|
||
**Method details:**
|
||
|
||
- `_mk_panel(side)`:
|
||
- Checks if panel is enabled in config
|
||
- Creates resizer with command and data attributes
|
||
- Creates hide icon with toggle command
|
||
- Applies `mf-hidden` class if panel is not visible
|
||
- Returns None if panel is disabled
|
||
|
||
- `_mk_show_icon(side)`:
|
||
- Checks if panel is enabled in config
|
||
- Returns None if panel is disabled or visible
|
||
- Applies `hidden` (Tailwind) class if panel is visible
|
||
- Applies positioning class based on side
|
||
|
||
### JavaScript Integration
|
||
|
||
The Panel component uses JavaScript for manual resizing:
|
||
|
||
**initResizer(panelId):**
|
||
|
||
- Initializes drag-and-drop resize functionality
|
||
- Adds/removes `no-transition` class during drag
|
||
- Sends width updates to server via HTMX
|
||
- Constrains width between 150px and 500px
|
||
|
||
**File:** `src/myfasthtml/assets/myfasthtml.js`
|