icon.rs
⎇
Raw
1use leptos::prelude::*;
2
3#[derive(Clone, Copy, PartialEq, Debug)]
4// Several variants are used from later milestones (context menu icons).
5#[allow(dead_code)]
6pub enum IconName {
7 Folder,
8 File,
9 Image,
10 Video,
11 Audio,
12 Text,
13 Code,
14 Archive,
15 Pdf,
16 Grid,
17 List,
18 Refresh,
19 Upload,
20 FolderPlus,
21 Download,
22 Pencil,
23 Move,
24 Copy,
25 Share,
26 Info,
27 Trash,
28}
29
30/// Pick the icon for a file name based on its extension.
31pub fn icon_for(name: &str, is_dir: bool) -> IconName {
32 if is_dir {
33 return IconName::Folder;
34 }
35 let ext = name.rsplit('.').next().unwrap_or("").to_lowercase();
36 match ext.as_str() {
37 "png" | "jpg" | "jpeg" | "gif" | "webp" | "svg" | "bmp" | "ico" | "avif" => {
38 IconName::Image
39 }
40 "mp4" | "webm" | "mkv" | "mov" | "avi" => IconName::Video,
41 "mp3" | "wav" | "ogg" | "flac" | "m4a" | "opus" => IconName::Audio,
42 "pdf" => IconName::Pdf,
43 "zip" | "tar" | "gz" | "tgz" | "zst" | "bz2" | "xz" | "7z" | "rar" => {
44 IconName::Archive
45 }
46 "rs" | "js" | "ts" | "tsx" | "jsx" | "py" | "rb" | "go" | "c" | "cpp"
47 | "h" | "hpp" | "json" | "toml" | "yaml" | "yml" | "sh" | "html" | "css"
48 | "wasm" | "sql" => IconName::Code,
49 "txt" | "md" | "log" | "csv" | "ini" | "conf" | "xml" => IconName::Text,
50 _ => IconName::File,
51 }
52}
53
54fn icon_d(name: IconName) -> &'static str {
55 match name {
56 IconName::Folder => {
57 "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"
58 }
59 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",
60 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",
61 IconName::Video => "M3 5h18v14H3z M10 9l5 3-5 3z",
62 IconName::Audio => {
63 "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"
64 }
65 IconName::Text => "M5 3h14v18H5z M9 8h6 M9 12h6 M9 16h4",
66 IconName::Code => "M8 8l-4 4 4 4 M16 8l4 4-4 4",
67 IconName::Archive => "M4 4h16v16H4z M12 4v2 M12 8v2 M12 12v2 M9 16h6v4H9z",
68 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",
69 IconName::Grid => "M4 4h7v7H4z M13 4h7v7h-7z M4 13h7v7H4z M13 13h7v7h-7z",
70 IconName::List => "M4 6h2 M9 6h11 M4 12h2 M9 12h11 M4 18h2 M9 18h11",
71 IconName::Refresh => "M21 12a9 9 0 1 1-3-6.7 M21 3v6h-6",
72 IconName::Upload => "M12 16V4 M7 9l5-5 5 5 M4 20h16",
73 IconName::FolderPlus => {
74 "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"
75 }
76 IconName::Download => "M12 4v12 M7 11l5 5 5-5 M4 20h16",
77 IconName::Pencil => "M4 20l1-4L16 5l3 3L8 19z M13 8l3 3",
78 IconName::Move => "M20 7H6 M9 4L6 7l3 3 M4 17h14 M15 14l3 3-3 3",
79 IconName::Copy => "M8 8h12v12H8z M4 16V4h12",
80 IconName::Share => "M4 13v6a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-6 M12 3v11 M8 7l4-4 4 4",
81 IconName::Info => "M12 3a9 9 0 1 0 .01 0z M12 11v5 M12 8h.01",
82 IconName::Trash => "M4 7h16 M9 7V4h6v3 M6 7l1 13h10l1-13 M10 11v6 M14 11v6",
83 }
84}
85
86fn icon_class(name: IconName) -> &'static str {
87 match name {
88 IconName::Folder | IconName::FolderPlus => "ic-folder",
89 _ => "ic-muted",
90 }
91}
92
93#[component]
94pub fn Icon(name: IconName, class: Option<String>) -> impl IntoView {
95 let mut cls = String::from("icon ");
96 cls.push_str(icon_class(name));
97 if let Some(extra) = class {
98 cls.push(' ');
99 cls.push_str(&extra);
100 }
101 view! {
102 <svg
103 class=cls
104 viewBox="0 0 24 24"
105 fill="none"
106 stroke="currentColor"
107 stroke_width="1.8"
108 stroke_linecap="round"
109 stroke_linejoin="round"
110 aria-hidden="true"
111 >
112 <path d=icon_d(name)/>
113 </svg>
114 }
115}
116