nemo-plugin 0.6.0

Plugin development kit for Nemo with fluent UI builders
Documentation
[![Documentation][docsrs-badge]][docsrs-url]

[docsrs-badge]: https://docs.rs/nemo-plugin/badge.svg
[docsrs-url]: https://docs.rs/nemo-plugin

# nemo-plugin

A Rust library providing a fluent, type-safe builder API for developing Nemo plugins. This crate builds on top of `nemo-plugin-api` to make creating UI layouts and templates more ergonomic and less error-prone.

## Features

- **Fluent Builder Pattern**: Chain method calls for clean, readable code
- **Type-Safe**: Compile-time checks for component properties
- **Component Builders**: Pre-built helpers for common UI elements
- **Container Builders**: Layout containers like Panel, Stack, Grid
- **Helper Functions**: Utilities for common patterns like input rows

## Installation

Add this to your plugin's `Cargo.toml`:

```toml
[dependencies]
nemo-plugin = "0.5"
```

## Quick Start

```rust
use nemo_plugin::prelude::*;

fn build_ui() -> PluginValue {
    Panel::new()
        .padding(16)
        .border(2)
        .border_color("theme.border")
        .width(300)
        .child("title", Label::new("My Plugin").size("xl"))
        .child("input", Input::new()
            .placeholder("Enter value")
            .on_change("on_input_change"))
        .child("button", Button::new("Submit")
            .variant("primary")
            .on_click("on_submit"))
        .build()
}
```

## Component Builders

### Label

```rust
Label::new("Hello World")
    .size("lg")
    .color("theme.primary")
    .weight("bold")
    .bind_text("data.message")
```

### Input

```rust
Input::new()
    .value("default")
    .placeholder("Type here...")
    .on_change("handle_change")
    .bind_value("data.input")
```

### Button

```rust
Button::new("Click Me")
    .variant("primary")
    .on_click("handle_click")
    .disabled(false)
```

### Switch

```rust
Switch::new()
    .label("Enable Feature")
    .checked(false)
    .on_click("toggle_feature")
    .bind_checked("data.enabled")
```

### Slider

```rust
Slider::new()
    .value(50.0)
    .min(0.0)
    .max(100.0)
    .step(1.0)
    .on_change("handle_slider")
```

## Container Builders

### Panel

A bordered container with optional padding and shadow:

```rust
Panel::new()
    .padding(16)
    .margin(8)
    .border(2)
    .border_color("theme.border")
    .shadow("md")
    .bg_color("theme.surface")
    .child("content", Label::new("Content"))
```

### Stack

Horizontal or vertical layout container:

```rust
// Vertical stack
Stack::vertical()
    .spacing(8)
    .align("start")
    .justify("center")
    .child("item1", Label::new("Item 1"))
    .child("item2", Label::new("Item 2"))

// Horizontal stack
Stack::horizontal()
    .spacing(12)
    .child("left", Label::new("Left"))
    .child("right", Label::new("Right"))
```

### Grid

Grid layout with configurable columns:

```rust
Grid::new(3)  // 3 columns
    .gap(8)
    .padding(16)
    .child("cell1", Label::new("Cell 1"))
    .child("cell2", Label::new("Cell 2"))
    .child("cell3", Label::new("Cell 3"))
```

### ScrollView

Scrollable container:

```rust
ScrollView::new()
    .direction("vertical")
    .width(400)
    .height(300)
    .child("content", Stack::vertical()
        .spacing(8)
        // ... many children
    )
```

## Helper Functions

### Input Row

Create a label + input combo:

```rust
use nemo_plugin::containers::input_row;

let row = input_row(
    "Username",           // label text
    Some(120),           // label width
    Input::new()         // component
        .placeholder("Enter username")
        .on_change("on_username"),
    Some(8),            // spacing
);
```

### Row and Column Helpers

```rust
use nemo_plugin::containers::{row, column};

// Quick horizontal row
let my_row = row(8)
    .child("label", Label::new("Name:"))
    .child("input", Input::new());

// Quick vertical column
let my_column = column(12)
    .child("header", Label::new("Title"))
    .child("body", Label::new("Content"));
```

## Common Patterns

### Form Layout

```rust
fn build_form() -> PluginValue {
    Panel::new()
        .padding(20)
        .child("form", Stack::vertical()
            .spacing(12)
            .child("name_row", input_row(
                "Name", Some(100),
                Input::new().on_change("on_name_change"),
                Some(8)
            ))
            .child("email_row", input_row(
                "Email", Some(100),
                Input::new().on_change("on_email_change"),
                Some(8)
            ))
            .child("submit", Button::new("Submit")
                .variant("primary")
                .on_click("on_submit"))
        )
        .build()
}
```

### Settings Panel

```rust
fn build_settings() -> PluginValue {
    Panel::new()
        .padding(16)
        .border(1)
        .child("settings", Stack::vertical()
            .spacing(16)
            .child("title", Label::new("Settings").size("xl"))
            .child("dark_mode", Switch::new()
                .label("Dark Mode")
                .bind_checked("settings.dark_mode"))
            .child("notifications", Switch::new()
                .label("Enable Notifications")
                .bind_checked("settings.notifications"))
            .child("volume", Stack::vertical()
                .spacing(4)
                .child("label", Label::new("Volume"))
                .child("slider", Slider::new()
                    .min(0.0)
                    .max(100.0)
                    .bind_value("settings.volume"))
            )
        )
        .build()
}
```

### Data Dashboard

```rust
fn build_dashboard() -> PluginValue {
    Panel::new()
        .padding(20)
        .child("dashboard", Grid::new(2)
            .gap(16)
            .child("temp", Panel::new()
                .padding(12)
                .border(1)
                .child("content", Stack::vertical()
                    .spacing(8)
                    .child("label", Label::new("Temperature"))
                    .child("value", Label::new("--")
                        .size("xl")
                        .bind_text("data.temperature"))
                )
            )
            .child("humidity", Panel::new()
                .padding(12)
                .border(1)
                .child("content", Stack::vertical()
                    .spacing(8)
                    .child("label", Label::new("Humidity"))
                    .child("value", Label::new("--")
                        .size("xl")
                        .bind_text("data.humidity"))
                )
            )
        )
        .build()
}
```

## Advanced Usage

### Custom Attributes

If you need to set custom attributes not covered by the builder methods:

```rust
use nemo_plugin::builder::{LayoutBuilder, Builder};

let custom = LayoutBuilder::new("my-custom-component")
    .attr("custom_prop", PluginValue::String("value".into()))
    .attr("numeric_prop", PluginValue::Integer(42))
    .build();
```

### Event Handlers

All components support the `.on()` method for custom events:

```rust
Button::new("Click")
    .on("click", "handle_click")
    .on("hover", "handle_hover")
    .on("focus", "handle_focus")
```

### Data Binding

Use `.bind()` for custom bindings:

```rust
use nemo_plugin::builder::{LayoutBuilder, Builder};

LayoutBuilder::new("label")
    .attr("text", PluginValue::String("Loading...".into()))
    .bind("text", "data.dynamic_value")
    .bind("color", "theme.text_color")
    .build()
```

## Comparison: Before and After

### Before (manual PluginValue construction)

```rust
fn build_old_way() -> PluginValue {
    let mut label_map = IndexMap::new();
    label_map.insert("type".to_string(), PluginValue::String("label".into()));
    label_map.insert("text".to_string(), PluginValue::String("Hello".into()));
    label_map.insert("width".to_string(), PluginValue::Integer(120));
    
    let mut input_map = IndexMap::new();
    input_map.insert("type".to_string(), PluginValue::String("input".into()));
    input_map.insert("value".to_string(), PluginValue::String("default".into()));
    input_map.insert("on_change".to_string(), PluginValue::String("handler".into()));
    
    let mut children = IndexMap::new();
    children.insert("label".to_string(), PluginValue::Object(label_map));
    children.insert("input".to_string(), PluginValue::Object(input_map));
    
    let mut container = IndexMap::new();
    container.insert("type".to_string(), PluginValue::String("stack".into()));
    container.insert("direction".to_string(), PluginValue::String("horizontal".into()));
    container.insert("spacing".to_string(), PluginValue::Integer(8));
    container.insert("component".to_string(), PluginValue::Object(children));
    
    PluginValue::Object(container)
}
```

### After (with nemo-plugin builders)

```rust
fn build_new_way() -> PluginValue {
    Stack::horizontal()
        .spacing(8)
        .child("label", Label::new("Hello").width(120))
        .child("input", Input::new()
            .value("default")
            .on_change("handler"))
        .build()
}
```

## API Reference

All builders implement the `Builder` trait:

```rust
pub trait Builder {
    fn build(self) -> PluginValue;
}
```

This allows them to be used interchangeably as children of container components.

## License

MIT OR Apache-2.0