/ web / src / pages / panels / shared_ui.rs
shared_ui.rs
 1  use dioxus::prelude::*;
 2  use lucide_dioxus::{Database, File, FileCode, FileSpreadsheet, FileText, Image};
 3  
 4  pub fn file_icon(name: &str) -> Element {
 5      let extension = name.rsplit('.').next().unwrap_or("").to_ascii_lowercase();
 6      match extension.as_str() {
 7          "js" | "rs" | "css" | "toml" => rsx! { FileCode { class: "w-4 h-4 text-primary" } },
 8          "html" | "htm" => rsx! { FileCode { class: "w-4 h-4 text-chart-3" } },
 9          "svg" | "png" | "jpg" => rsx! { Image { class: "w-4 h-4 text-chart-2" } },
10          "csv" | "tsv" => rsx! { FileSpreadsheet { class: "w-4 h-4 text-chart-4" } },
11          "db" | "sqlite" => rsx! { Database { class: "w-4 h-4 text-chart-4" } },
12          "txt" | "log" | "md" => rsx! { FileText { class: "w-4 h-4 text-muted-foreground" } },
13          "wasm" | "was" => rsx! { FileCode { class: "w-4 h-4 text-chart-5" } },
14          _ => rsx! { File { class: "w-4 h-4 text-muted-foreground" } },
15      }
16  }
17  
18  #[component]
19  pub fn LiveIndicator(connected: bool) -> Element {
20      let (dot_class, ping_class, label) = if connected {
21          ("bg-emerald-400", "absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-70 animate-ping", "LIVE")
22      } else {
23          ("bg-amber-500", "absolute inline-flex h-full w-full rounded-full bg-amber-500 opacity-70 animate-pulse", "POLLING")
24      };
25      rsx! {
26          div { class: "flex items-center gap-2 rounded-full border border-border bg-background/60 px-3 py-1.5 text-xs text-muted-foreground shrink-0",
27              span { class: "relative flex h-2 w-2",
28                  span { class: "{ping_class}" }
29                  span { class: "relative inline-flex h-2 w-2 rounded-full {dot_class}" }
30              }
31              span { class: "font-medium text-foreground", "{label}" }
32          }
33      }
34  }
35  
36  #[component]
37  pub fn StatusBadge(icon: Element, value: String) -> Element {
38      rsx! {
39          span { class: "inline-flex items-center gap-1.5 rounded-full border border-border bg-background/60 px-2.5 py-1 text-xs font-mono text-foreground",
40              {icon}
41              "{value}"
42          }
43      }
44  }
45  
46  #[component]
47  pub fn Th(children: Element) -> Element {
48      rsx! {
49          th { scope: "col", class: "text-left px-3 py-2 border-b border-border text-muted-foreground text-xs uppercase tracking-wider sticky top-0 bg-muted whitespace-nowrap",
50              {children}
51          }
52      }
53  }
54  
55  #[component]
56  pub fn Td(children: Element, class: Option<String>) -> Element {
57      let extra = class.unwrap_or_default();
58      rsx! {
59          td { class: "px-3 py-2 text-sm {extra}", {children} }
60      }
61  }