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