mirror of
https://github.com/cloud-shuttle/leptos-shadcn-ui.git
synced 2026-01-04 12:02:56 +00:00
404 lines
18 KiB
Plaintext
404 lines
18 KiB
Plaintext
//! Enhanced lazy loading component with realistic simulation
|
|
|
|
use leptos::*;
|
|
use leptos::prelude::*;
|
|
|
|
/// Component metadata for enhanced lazy loading
|
|
#[derive(Clone)]
|
|
struct ComponentInfo {
|
|
name: String,
|
|
category: String,
|
|
estimated_size: String,
|
|
dependencies: Vec<String>,
|
|
description: String,
|
|
}
|
|
|
|
/// Enhanced lazy component wrapper with realistic simulation
|
|
#[component]
|
|
pub fn LazyComponentWrapper(
|
|
#[prop(into)] name: String,
|
|
) -> impl IntoView {
|
|
let (is_loaded, set_is_loaded) = signal(false);
|
|
let (is_loading, set_is_loading) = signal(false);
|
|
let (load_progress, set_load_progress) = signal(0.0);
|
|
|
|
// Component metadata based on name
|
|
let component_info = move || {
|
|
match name.as_str() {
|
|
"Alert" => ComponentInfo {
|
|
name: "Alert".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "12KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Displays important messages to users".to_string(),
|
|
},
|
|
"Badge" => ComponentInfo {
|
|
name: "Badge".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "8KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Small status indicators and labels".to_string(),
|
|
},
|
|
"Checkbox" => ComponentInfo {
|
|
name: "Checkbox".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "15KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Interactive checkbox input component".to_string(),
|
|
},
|
|
"Combobox" => ComponentInfo {
|
|
name: "Combobox".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "25KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Searchable dropdown with custom options".to_string(),
|
|
},
|
|
"Form" => ComponentInfo {
|
|
name: "Form".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "35KB".to_string(),
|
|
dependencies: vec!["leptos-hook-form".to_string()],
|
|
description: "Complete form handling with validation".to_string(),
|
|
},
|
|
"Input OTP" => ComponentInfo {
|
|
name: "Input OTP".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "18KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "One-time password input fields".to_string(),
|
|
},
|
|
"Radio Group" => ComponentInfo {
|
|
name: "Radio Group".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "20KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Radio button group selection".to_string(),
|
|
},
|
|
"Select" => ComponentInfo {
|
|
name: "Select".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "22KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Dropdown selection component".to_string(),
|
|
},
|
|
"Slider" => ComponentInfo {
|
|
name: "Slider".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "16KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Range slider input component".to_string(),
|
|
},
|
|
"Switch" => ComponentInfo {
|
|
name: "Switch".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "14KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Toggle switch component".to_string(),
|
|
},
|
|
"Textarea" => ComponentInfo {
|
|
name: "Textarea".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "10KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Multi-line text input".to_string(),
|
|
},
|
|
"Toggle" => ComponentInfo {
|
|
name: "Toggle".to_string(),
|
|
category: "Form & Input".to_string(),
|
|
estimated_size: "12KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Button toggle component".to_string(),
|
|
},
|
|
"Accordion" => ComponentInfo {
|
|
name: "Accordion".to_string(),
|
|
category: "Layout & Navigation".to_string(),
|
|
estimated_size: "28KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Collapsible content sections".to_string(),
|
|
},
|
|
"Breadcrumb" => ComponentInfo {
|
|
name: "Breadcrumb".to_string(),
|
|
category: "Layout & Navigation".to_string(),
|
|
estimated_size: "18KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Navigation breadcrumb trail".to_string(),
|
|
},
|
|
"Collapsible" => ComponentInfo {
|
|
name: "Collapsible".to_string(),
|
|
category: "Layout & Navigation".to_string(),
|
|
estimated_size: "20KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Expandable content container".to_string(),
|
|
},
|
|
"Command" => ComponentInfo {
|
|
name: "Command".to_string(),
|
|
category: "Layout & Navigation".to_string(),
|
|
estimated_size: "32KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Command palette interface".to_string(),
|
|
},
|
|
"Navigation Menu" => ComponentInfo {
|
|
name: "Navigation Menu".to_string(),
|
|
category: "Layout & Navigation".to_string(),
|
|
estimated_size: "40KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Complex navigation menu system".to_string(),
|
|
},
|
|
"Pagination" => ComponentInfo {
|
|
name: "Pagination".to_string(),
|
|
category: "Layout & Navigation".to_string(),
|
|
estimated_size: "25KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Page navigation controls".to_string(),
|
|
},
|
|
"Scroll Area" => ComponentInfo {
|
|
name: "Scroll Area".to_string(),
|
|
category: "Layout & Navigation".to_string(),
|
|
estimated_size: "15KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Custom scrollable container".to_string(),
|
|
},
|
|
"Skeleton" => ComponentInfo {
|
|
name: "Skeleton".to_string(),
|
|
category: "Layout & Navigation".to_string(),
|
|
estimated_size: "12KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Loading placeholder components".to_string(),
|
|
},
|
|
"Tabs" => ComponentInfo {
|
|
name: "Tabs".to_string(),
|
|
category: "Layout & Navigation".to_string(),
|
|
estimated_size: "30KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Tabbed content interface".to_string(),
|
|
},
|
|
"Alert Dialog" => ComponentInfo {
|
|
name: "Alert Dialog".to_string(),
|
|
category: "Overlay & Feedback".to_string(),
|
|
estimated_size: "35KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Modal dialog with actions".to_string(),
|
|
},
|
|
"Dialog" => ComponentInfo {
|
|
name: "Dialog".to_string(),
|
|
category: "Overlay & Feedback".to_string(),
|
|
estimated_size: "30KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Modal dialog component".to_string(),
|
|
},
|
|
"Drawer" => ComponentInfo {
|
|
name: "Drawer".to_string(),
|
|
category: "Overlay & Feedback".to_string(),
|
|
estimated_size: "38KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Slide-out drawer panel".to_string(),
|
|
},
|
|
"Dropdown Menu" => ComponentInfo {
|
|
name: "Dropdown Menu".to_string(),
|
|
category: "Overlay & Feedback".to_string(),
|
|
estimated_size: "28KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Contextual dropdown menu".to_string(),
|
|
},
|
|
"Hover Card" => ComponentInfo {
|
|
name: "Hover Card".to_string(),
|
|
category: "Overlay & Feedback".to_string(),
|
|
estimated_size: "22KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Hover-triggered information card".to_string(),
|
|
},
|
|
"Menubar" => ComponentInfo {
|
|
name: "Menubar".to_string(),
|
|
category: "Overlay & Feedback".to_string(),
|
|
estimated_size: "45KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Horizontal menu bar".to_string(),
|
|
},
|
|
"Popover" => ComponentInfo {
|
|
name: "Popover".to_string(),
|
|
category: "Overlay & Feedback".to_string(),
|
|
estimated_size: "20KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Positioned popup content".to_string(),
|
|
},
|
|
"Sheet" => ComponentInfo {
|
|
name: "Sheet".to_string(),
|
|
category: "Overlay & Feedback".to_string(),
|
|
estimated_size: "32KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Slide-up sheet panel".to_string(),
|
|
},
|
|
"Toast" => ComponentInfo {
|
|
name: "Toast".to_string(),
|
|
category: "Overlay & Feedback".to_string(),
|
|
estimated_size: "25KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Notification toast messages".to_string(),
|
|
},
|
|
"Tooltip" => ComponentInfo {
|
|
name: "Tooltip".to_string(),
|
|
category: "Overlay & Feedback".to_string(),
|
|
estimated_size: "18KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Hover tooltip component".to_string(),
|
|
},
|
|
"Aspect Ratio" => ComponentInfo {
|
|
name: "Aspect Ratio".to_string(),
|
|
category: "Data & Media".to_string(),
|
|
estimated_size: "8KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Maintains aspect ratio container".to_string(),
|
|
},
|
|
"Calendar" => ComponentInfo {
|
|
name: "Calendar".to_string(),
|
|
category: "Data & Media".to_string(),
|
|
estimated_size: "50KB".to_string(),
|
|
dependencies: vec!["chrono".to_string()],
|
|
description: "Interactive calendar component".to_string(),
|
|
},
|
|
"Carousel" => ComponentInfo {
|
|
name: "Carousel".to_string(),
|
|
category: "Data & Media".to_string(),
|
|
estimated_size: "35KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Image/content carousel".to_string(),
|
|
},
|
|
"Context Menu" => ComponentInfo {
|
|
name: "Context Menu".to_string(),
|
|
category: "Data & Media".to_string(),
|
|
estimated_size: "30KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Right-click context menu".to_string(),
|
|
},
|
|
"Date Picker" => ComponentInfo {
|
|
name: "Date Picker".to_string(),
|
|
category: "Data & Media".to_string(),
|
|
estimated_size: "45KB".to_string(),
|
|
dependencies: vec!["chrono".to_string()],
|
|
description: "Date selection component".to_string(),
|
|
},
|
|
"Progress" => ComponentInfo {
|
|
name: "Progress".to_string(),
|
|
category: "Data & Media".to_string(),
|
|
estimated_size: "12KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Progress bar component".to_string(),
|
|
},
|
|
"Table" => ComponentInfo {
|
|
name: "Table".to_string(),
|
|
category: "Data & Media".to_string(),
|
|
estimated_size: "40KB".to_string(),
|
|
dependencies: vec!["lucide-leptos".to_string()],
|
|
description: "Data table with sorting".to_string(),
|
|
},
|
|
_ => ComponentInfo {
|
|
name: name.clone(),
|
|
category: "Unknown".to_string(),
|
|
estimated_size: "20KB".to_string(),
|
|
dependencies: vec![],
|
|
description: "Component description not available".to_string(),
|
|
},
|
|
}
|
|
};
|
|
|
|
let load_component = move |_| {
|
|
set_is_loading.set(true);
|
|
set_load_progress.set(0.0);
|
|
|
|
// Simulate loading progress
|
|
let progress_interval = set_interval_with_handle(
|
|
move || {
|
|
set_load_progress.update(|p| {
|
|
if *p < 100.0 {
|
|
*p += 10.0;
|
|
} else {
|
|
set_is_loading.set(false);
|
|
set_is_loaded.set(true);
|
|
}
|
|
});
|
|
},
|
|
std::time::Duration::from_millis(100),
|
|
).unwrap();
|
|
|
|
// Clean up interval after loading
|
|
spawn_local(async move {
|
|
gloo_timers::future::TimeoutFuture::new(1000).await;
|
|
progress_interval.clear();
|
|
});
|
|
};
|
|
|
|
view! {
|
|
<div class="lazy-component-wrapper">
|
|
<div class="component-header">
|
|
<h4>{name.clone()}</h4>
|
|
<div class="component-meta">
|
|
<span class="component-category">{component_info().category}</span>
|
|
<span class="component-size">{component_info().estimated_size}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="component-content">
|
|
<div class="lazy-component-loaded" class:hidden={move || !is_loaded.get()}>
|
|
<div class="component-success">
|
|
<div class="success-icon">"✅"</div>
|
|
<p class="success-text">"Component loaded successfully!"</p>
|
|
<div class="component-demo">
|
|
<span>"🎉 {name} is now available"</span>
|
|
</div>
|
|
<div class="component-details">
|
|
<p class="component-description">{component_info().description}</p>
|
|
<div class="component-dependencies">
|
|
<strong>"Dependencies:"</strong>
|
|
{if component_info().dependencies.is_empty() {
|
|
"None".to_string()
|
|
} else {
|
|
component_info().dependencies.join(", ")
|
|
}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="component-loading" class:hidden={move || !is_loading.get()}>
|
|
<div class="loading-content">
|
|
<div class="loading-spinner"></div>
|
|
<p>"Loading {name}..."</p>
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" style={format!("width: {}%", load_progress.get())}></div>
|
|
</div>
|
|
<span class="progress-text">{move || format!("{}%", load_progress.get() as i32)}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="component-placeholder" class:hidden={move || is_loaded.get() || is_loading.get()}>
|
|
<div class="placeholder-content">
|
|
<p class="placeholder-text">"This component is not yet loaded. Click to load it on demand."</p>
|
|
<div class="component-preview">
|
|
<p class="preview-description">{component_info().description}</p>
|
|
<div class="preview-meta">
|
|
<span class="preview-size">"Size: {component_info().estimated_size}"</span>
|
|
<span class="preview-category">"Category: {component_info().category}"</span>
|
|
</div>
|
|
</div>
|
|
<button on:click={load_component} class="load-btn">
|
|
"Load {name}"
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
/// Simple lazy loading provider
|
|
#[component]
|
|
pub fn LazyLoadingProvider(
|
|
#[prop(into)] children: Children,
|
|
) -> impl IntoView {
|
|
view! {
|
|
<div class="lazy-loading-provider">
|
|
{children()}
|
|
</div>
|
|
}
|
|
}
|