A flexible disclosure/accordion widget for ratatui
| .config | chore: initial commit | |
| docs | chore: initial commit | |
| examples | feat: add example | |
| src | chore: initial commit | |
| .copier-answers.yml | chore: initial commit | |
| .editorconfig | chore: initial commit | |
| .envrc | chore: initial commit | |
| .gitignore | chore: initial commit | |
| .ignore | chore: initial commit | |
| biome.json | chore: initial commit | |
| Cargo.lock | feat: add example | |
| Cargo.toml | feat: add example | |
| CLAUDE.md | chore: initial commit | |
| cog.toml | chore: initial commit | |
| COPYING | chore: initial commit | |
| flake.lock | chore: initial commit | |
| flake.nix | chore: initial commit | |
| Justfile | chore: initial commit | |
| README.md | docs: update | |
| rustfmt.toml | chore: initial commit | |
| treefmt.toml | chore: initial commit | |
disclotuire
A flexible disclosure/accordion widget for ratatui with support for nested panes and keyboard navigation.
Features
- Multiple simultaneously open panes
- Nested disclosure panes with independent collapse/expand state
- Configurable indentation depth and expand/collapse indicators
- Path-based state tracking for efficient rendering
Usage
Basic Example
usedisclotuire::{Disclosure,DisclosureState,Pane};// Create panes
letpanes=vec![Pane::new_leaf("Simple Pane"),Pane::new("Parent Pane",vec![Pane::new_leaf("Child 1"),Pane::new_leaf("Child 2"),],),];// Create state with panes
letmutstate=DisclosureState::new(panes);// Create widget with rendering configuration
letwidget=Disclosure::new().indent_size(2).collapsed_indicator("▶").expanded_indicator("▼");// In your draw loop
frame.render_stateful_widget(widget,area,&mutstate);Navigation
// Handle keyboard events
matchkey.code{KeyCode::Up=>state.navigate_prev(),KeyCode::Down=>state.navigate_next(),KeyCode::Left=>state.collapse(),KeyCode::Right=>state.expand(),KeyCode::Enter|KeyCode::Char(' ')=>state.toggle(),_=>{}}Using with RefCell for Interior Mutability
When implementing Widget for &App, you'll need RefCell for interior mutability:
usestd::cell::RefCell;struct App{disclosure_state: RefCell<DisclosureState>,}implWidgetfor&App{fn render(self,area: Rect,buf: &mutBuffer){letwidget=Disclosure::new().indent_size(2).collapsed_indicator("▶").expanded_indicator("▼");StatefulWidget::render(widget,area,buf,&mutself.disclosure_state.borrow_mut());}}// In event handling (with &mut self)
fn handle_key(&mutself,key: KeyEvent){matchkey.code{KeyCode::Up=>self.disclosure_state.borrow_mut().navigate_prev(),KeyCode::Down=>self.disclosure_state.borrow_mut().navigate_next(),// ...
}}Configuration Options
// Configure widget appearance
letwidget=Disclosure::new().indent_size(3)// Spaces per indentation level
.collapsed_indicator("+").expanded_indicator("-");Running the Example
cargo run --example example
Controls:
- ↑/↓ or j/k - Navigate between visible pane titles
- ←/→ or h/l - Collapse/expand focused pane
- Space/Enter/Esc - Toggle focused pane
- q or Ctrl+C - Quit
Implementation Notes
Path-Based State
The widget uses Vec<usize> paths to identify panes. For example:
[0]- First root pane[1, 2]- Third child of second root pane[1, 2, 0]- First grandchild of that child
This allows efficient tracking of open/closed state and focus without requiring unique IDs.
Navigation Behavior
When navigating with Up/Down:
- Only visible pane titles are included in navigation
- Child panes are only visible when their parent is expanded
- Nested panes can be expanded/collapsed independently
Potential Enhancements
Some areas for future development:
- Dynamic tree modification (add/remove panes at runtime)
- Custom styling per pane or depth level
- Search/filter functionality
- Lazy loading for large trees
- Support for arbitrary widget content in panes
- Horizontal scrolling for long titles
- Mouse click support
- Auto-collapse children when parent is collapsed