use api_types::FileKind; use leptos::prelude::*; #[derive(Clone, Copy, PartialEq, Debug)] // Several variants are used from later milestones (context menu icons). #[allow(dead_code)] pub enum IconName { Folder, File, Image, Video, Audio, Text, Code, Archive, Pdf, Grid, List, Refresh, Upload, FolderPlus, Download, Pencil, Move, Copy, Share, Info, Trash, } /// Extensions that get the "code" icon instead of the plain-text one. Both are /// [`FileKind::Text`] to the server — code-vs-prose is a naming question, not a /// content one, so it stays here. const CODE_EXTS: &[&str] = &[ "rs", "js", "mjs", "cjs", "ts", "tsx", "jsx", "py", "rb", "go", "c", "cc", "cpp", "h", "hpp", "java", "kt", "cs", "php", "swift", "lua", "sh", "bash", "fish", "zsh", "ps1", "json", "toml", "yaml", "yml", "html", "htm", "css", "scss", "sql", "xml", "vue", "svelte", ]; /// Pick the icon for an entry from its server-sniffed [`FileKind`]. /// /// `name` is used only to split [`FileKind::Text`] into prose and code. pub fn icon_for(kind: FileKind, name: &str) -> IconName { match kind { FileKind::Dir => IconName::Folder, FileKind::Image => IconName::Image, FileKind::Video => IconName::Video, FileKind::Audio => IconName::Audio, FileKind::Pdf => IconName::Pdf, FileKind::Archive => IconName::Archive, FileKind::Text => { let ext = name.rsplit('.').next().unwrap_or("").to_lowercase(); if CODE_EXTS.contains(&ext.as_str()) { IconName::Code } else { IconName::Text } } FileKind::Binary => IconName::File, } } fn icon_d(name: IconName) -> &'static str { match name { IconName::Folder => { "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" } IconName::File => "M6 2h8l5 5v13a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2z M13 2v6h6", IconName::Image => { "M3 5h18v14H3z M10 10a1.5 1.5 0 1 1-3 0a1.5 1.5 0 0 1 3 0z M21 15l-5-5-9 9" } IconName::Video => "M3 5h18v14H3z M10 9l5 3-5 3z", IconName::Audio => { "M9 18V6l10-2v10 M9 18a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z M19 15a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z" } IconName::Text => "M5 3h14v18H5z M9 8h6 M9 12h6 M9 16h4", IconName::Code => "M8 8l-4 4 4 4 M16 8l4 4-4 4", IconName::Archive => "M4 4h16v16H4z M12 4v2 M12 8v2 M12 12v2 M9 16h6v4H9z", IconName::Pdf => { "M6 2h8l5 5v13a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2z M13 2v6h6 M8 13h8 M8 17h8" } IconName::Grid => "M4 4h7v7H4z M13 4h7v7h-7z M4 13h7v7H4z M13 13h7v7h-7z", IconName::List => "M4 6h2 M9 6h11 M4 12h2 M9 12h11 M4 18h2 M9 18h11", IconName::Refresh => "M21 12a9 9 0 1 1-3-6.7 M21 3v6h-6", IconName::Upload => "M12 16V4 M7 9l5-5 5 5 M4 20h16", IconName::FolderPlus => { "M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z M12 11v6 M9 14h6" } IconName::Download => "M12 4v12 M7 11l5 5 5-5 M4 20h16", IconName::Pencil => "M4 20l1-4L16 5l3 3L8 19z M13 8l3 3", IconName::Move => "M20 7H6 M9 4L6 7l3 3 M4 17h14 M15 14l3 3-3 3", IconName::Copy => "M8 8h12v12H8z M4 16V4h12", IconName::Share => "M4 13v6a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-6 M12 3v11 M8 7l4-4 4 4", IconName::Info => "M12 3a9 9 0 1 0 .01 0z M12 11v5 M12 8h.01", IconName::Trash => "M4 7h16 M9 7V4h6v3 M6 7l1 13h10l1-13 M10 11v6 M14 11v6", } } fn icon_class(name: IconName) -> &'static str { match name { IconName::Folder | IconName::FolderPlus => "ic-folder", _ => "ic-muted", } } #[component] pub fn Icon(name: IconName, class: Option) -> impl IntoView { let mut cls = String::from("icon "); cls.push_str(icon_class(name)); if let Some(extra) = class { cls.push(' '); cls.push_str(&extra); } view! { } }