windjammer-ui 0.3.6

Cross-platform UI framework for Windjammer (Web, Desktop, Mobile)
Documentation
// Button component - Pure Windjammer implementation
// Cross-platform: renders to HTML (web) or egui (desktop)
// NO `mut` keyword needed - compiler infers everything!

use super::traits::Renderable
use super::traits::RenderableVNode
use super::vnode::VNode

@auto
pub enum ButtonVariant {
    Primary,
    Secondary,
    Success,
    Danger,
    Warning,
    Ghost,
}

@auto
pub enum ButtonSize {
    Small,
    Medium,
    Large,
}

@auto
pub struct Button {
    label: string,
    variant: ButtonVariant,
    size: ButtonSize,
    disabled: bool,
}

impl Button {
    // Constructor - returns new Button
    pub fn new(label: string) -> Button {
        Button {
            label: label,
            variant: ButtonVariant::Primary,
            size: ButtonSize::Medium,
            disabled: false,
        }
    }
    
    // Builder pattern: consumes self, modifies, returns self
    // Compiler infers: mut self (owned, mutable)
    pub fn variant(self, variant: ButtonVariant) -> Button {
        self.variant = variant
        self
    }
    
    // Builder pattern: consumes self, modifies, returns self  
    // Compiler infers: mut self (owned, mutable)
    pub fn size(self, size: ButtonSize) -> Button {
        self.size = size
        self
    }
    
    // Builder pattern: consumes self, modifies, returns self  
    // Compiler infers: mut self (owned, mutable)
    pub fn disabled(self, disabled: bool) -> Button {
        self.disabled = disabled
        self
    }
    
    // Get variant CSS class
    fn get_variant_class(&self) -> string {
        match self.variant {
            ButtonVariant::Primary => "wj-button-primary".to_string(),
            ButtonVariant::Secondary => "wj-button-secondary".to_string(),
            ButtonVariant::Success => "wj-button-success".to_string(),
            ButtonVariant::Danger => "wj-button-danger".to_string(),
            ButtonVariant::Warning => "wj-button-warning".to_string(),
            ButtonVariant::Ghost => "wj-button-ghost".to_string(),
        }
    }
    
    // Get size CSS class
    fn get_size_class(&self) -> string {
        match self.size {
            ButtonSize::Small => "wj-button-sm".to_string(),
            ButtonSize::Medium => "wj-button-md".to_string(),
            ButtonSize::Large => "wj-button-lg".to_string(),
        }
    }
    
    // Get inline style based on variant and size
    fn get_style(&self) -> string {
        let base = "border: none; border-radius: 4px; cursor: pointer; font-weight: 500; transition: all 0.2s;".to_string()
        
        let size_style = match self.size {
            ButtonSize::Small => " padding: 4px 8px; font-size: 12px;",
            ButtonSize::Medium => " padding: 8px 16px; font-size: 14px;",
            ButtonSize::Large => " padding: 12px 24px; font-size: 16px;",
        }
        
        let variant_style = match self.variant {
            ButtonVariant::Primary => " background: #4A9EFF; color: white;",
            ButtonVariant::Secondary => " background: #333; color: #e0e0e0; border: 1px solid #555;",
            ButtonVariant::Success => " background: #44AA44; color: white;",
            ButtonVariant::Danger => " background: #FF4444; color: white;",
            ButtonVariant::Warning => " background: #FFAA44; color: white;",
            ButtonVariant::Ghost => " background: transparent; color: #4A9EFF;",
        }
        
        let disabled_style = if self.disabled {
            " opacity: 0.5; cursor: not-allowed;"
        } else {
            ""
        }
        
        format!("{}{}{}{}", base, size_style, variant_style, disabled_style)
    }
}

// Implement RenderableVNode for cross-platform rendering
impl RenderableVNode for Button {
    fn to_vnode(&self) -> VNode {
        VNode::button()
            .add_class("wj-button".to_string())
            .add_class(self.get_variant_class())
            .add_class(self.get_size_class())
            .add_style(self.get_style())
            .set_disabled(self.disabled)
            .add_text(self.label.clone())
    }
}

// Implement Renderable for backward compatibility (HTML string output)
impl Renderable for Button {
    fn render(self) -> string {
        // Build variant class
        let variant_class = match self.variant {
            ButtonVariant::Primary => "wj-button-primary",
            ButtonVariant::Secondary => "wj-button-secondary",
            ButtonVariant::Success => "wj-button-success",
            ButtonVariant::Danger => "wj-button-danger",
            ButtonVariant::Warning => "wj-button-warning",
            ButtonVariant::Ghost => "wj-button-ghost",
        }
        
        // Build size class
        let size_class = match self.size {
            ButtonSize::Small => "wj-button-sm",
            ButtonSize::Medium => "wj-button-md",
            ButtonSize::Large => "wj-button-lg",
        }
        
        // Build disabled attr
        let disabled_attr = if self.disabled { " disabled='true'" } else { "" }
        
        format!("<button class='wj-button {} {}' style='{}'{}>{}</button>", 
                variant_class, size_class, self.get_style(), disabled_attr, self.label)
    }
}

// Test the Button component
fn main() {
    let button1 = Button::new("Click Me".to_string())
        .variant(ButtonVariant::Primary)
        .size(ButtonSize::Large)
    
    let button2 = Button::new("Cancel".to_string())
        .variant(ButtonVariant::Danger)
        .disabled(true)
    
    // Test both rendering methods
    println!("Button 1 (HTML): {}", button1.clone().render())
    println!("Button 2 (HTML): {}", button2.clone().render())
    
    // Test VNode rendering
    let vnode1 = button1.to_vnode()
    let vnode2 = button2.to_vnode()
    println!("Button 1 VNode handle: {}", vnode1.raw_handle())
    println!("Button 2 VNode handle: {}", vnode2.raw_handle())
}