dioxus-ratatui/src/layout.rs
Tyler King da7b4c5658 feat: dioxus-ratatui v0.1.0 — Ratatui renderer for Dioxus
Bridge between Dioxus Core (reactive components) and
Ratatui (terminal rendering).

Architecture: retained TuiNode tree + immediate Ratatui render
  Dioxus VirtualDom → WriteMutations → TuiTree → Frame → Terminal

Modules:
  tree.rs      — retained node tree (Element, Text, Placeholder)
  renderer.rs  — WriteMutations impl (14 methods)
  layout.rs    — Dioxus attrs → Ratatui Constraint/Direction
  style.rs     — attrs → Ratatui Style (colors, bold, italic)
  events.rs    — crossterm quit detection
  render.rs    — tree walker → Ratatui widget rendering
  lib.rs       — launch() + event loop + terminal lifecycle

Elements: div, p, span, h1-h3, hr
Layout: vertical/horizontal split, percentage/length/fill constraints
Styling: named colors, hex, bold, italic, underline, dim
Events: keyboard quit (Ctrl+C, q)

453 lines. Zero governance/substrate dependencies.
Apache 2.0 — pure community crate.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-05 14:38:14 -04:00

40 lines
1.5 KiB
Rust

//! Map Dioxus layout attributes to Ratatui Layout/Constraint.
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use std::collections::HashMap;
pub fn direction_from_attrs(attrs: &HashMap<String, String>) -> Direction {
match attrs.get("flex_direction").or(attrs.get("direction")).map(|s| s.as_str()) {
Some("row") | Some("horizontal") => Direction::Horizontal,
_ => Direction::Vertical,
}
}
pub fn constraint_from_attrs(attrs: &HashMap<String, String>, direction: Direction) -> Constraint {
let size_attr = match direction {
Direction::Vertical => attrs.get("height"),
Direction::Horizontal => attrs.get("width"),
};
if let Some(size) = size_attr {
return parse_constraint(size);
}
if let Some(flex) = attrs.get("flex") {
if let Ok(r) = flex.parse::<u16>() { return Constraint::Fill(r); }
}
Constraint::Fill(1)
}
fn parse_constraint(s: &str) -> Constraint {
let s = s.trim();
if s.ends_with('%') {
if let Ok(p) = s.trim_end_matches('%').parse::<u16>() { return Constraint::Percentage(p); }
}
let numeric = s.trim_end_matches(|c: char| c.is_alphabetic());
if let Ok(n) = numeric.parse::<u16>() { return Constraint::Length(n); }
Constraint::Fill(1)
}
pub fn split_area(area: Rect, direction: Direction, constraints: &[Constraint]) -> Vec<Rect> {
if constraints.is_empty() { return vec![area]; }
Layout::default().direction(direction).constraints(constraints).split(area).to_vec()
}