Skip to content

Commit 2828ce4

Browse files
authored
feat: add official fileIcons API and Icon pack setting (#2887)
* WIP * feat: improve the api and its use in other part of app * fix * format * chore: i18n strings * fix: ownership and recycle name * fix: listeners leak * address the overhead of matchExtenstion * format
1 parent 6d8ad2d commit 2828ce4

55 files changed

Lines changed: 2434 additions & 112 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/components/fileTree/index.js

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ export default class FileTree {
127127
$title.dataset.name = name;
128128
const textEl = $title.querySelector(".text");
129129
if (textEl) textEl.textContent = name;
130+
const iconEl = $title.querySelector("span:first-child");
131+
if (iconEl) {
132+
iconEl.className = helpers.getIconForFolder(name, {
133+
expanded: false,
134+
});
135+
}
130136

131137
// Collapse if expanded and clear children
132138
if (!recycledEl.classList.contains("hidden")) {
@@ -137,6 +143,7 @@ export default class FileTree {
137143
this.childTrees.delete(recycledEl._folderUrl);
138144
}
139145
recycledEl.$ul.innerHTML = "";
146+
recycledEl.$ul._fileTree = null;
140147
}
141148

142149
recycledEl._folderUrl = url;
@@ -149,7 +156,9 @@ export default class FileTree {
149156
});
150157
$wrapper._folderUrl = url;
151158

152-
const $indicator = tag("span", { className: "icon folder" });
159+
const $indicator = tag("span", {
160+
className: helpers.getIconForFolder(name, { expanded: false }),
161+
});
153162

154163
const $title = tile({
155164
lead: $indicator,
@@ -166,15 +175,20 @@ export default class FileTree {
166175
$wrapper.append($title, $content);
167176

168177
// Child file tree for nested folders
169-
let childTree = null;
170178
$content._fileTree = null;
171179

172180
const toggle = async () => {
181+
const name = $title.dataset.name;
182+
const url = $title.dataset.url;
173183
const isExpanded = !$wrapper.classList.contains("hidden");
184+
let childTree = $content._fileTree;
174185

175186
if (isExpanded) {
176187
// Collapse
177188
$wrapper.classList.add("hidden");
189+
$indicator.className = helpers.getIconForFolder(name, {
190+
expanded: false,
191+
});
178192

179193
if (childTree) {
180194
childTree.destroy();
@@ -186,6 +200,9 @@ export default class FileTree {
186200
} else {
187201
// Expand
188202
$wrapper.classList.remove("hidden");
203+
$indicator.className = helpers.getIconForFolder(name, {
204+
expanded: true,
205+
});
189206
$title.classList.add("loading");
190207

191208
// Create child tree with incremented depth
@@ -211,7 +228,12 @@ export default class FileTree {
211228

212229
$title.addEventListener("contextmenu", (e) => {
213230
e.stopPropagation();
214-
this.options.onContextMenu?.("dir", url, name, $title);
231+
this.options.onContextMenu?.(
232+
"dir",
233+
$title.dataset.url,
234+
$title.dataset.name,
235+
$title,
236+
);
215237
});
216238

217239
// Check if folder should be expanded from saved state
@@ -225,9 +247,9 @@ export default class FileTree {
225247
expanded: { get: () => !$wrapper.classList.contains("hidden") },
226248
unclasped: { get: () => !$wrapper.classList.contains("hidden") }, // Legacy compatibility
227249
$ul: { get: () => $content },
228-
fileTree: { get: () => childTree },
250+
fileTree: { get: () => $content._fileTree },
229251
refresh: {
230-
value: () => childTree?.refresh(),
252+
value: () => $content._fileTree?.refresh(),
231253
},
232254
expand: {
233255
value: () => !$wrapper.classList.contains("hidden") || toggle(),
@@ -284,12 +306,17 @@ export default class FileTree {
284306

285307
$tile.addEventListener("click", (e) => {
286308
e.stopPropagation();
287-
this.options.onFileClick?.(url, name);
309+
this.options.onFileClick?.($tile.dataset.url, $tile.dataset.name);
288310
});
289311

290312
$tile.addEventListener("contextmenu", (e) => {
291313
e.stopPropagation();
292-
this.options.onContextMenu?.("file", url, name, $tile);
314+
this.options.onContextMenu?.(
315+
"file",
316+
$tile.dataset.url,
317+
$tile.dataset.name,
318+
$tile,
319+
);
293320
});
294321

295322
return $tile;

src/components/referencesPanel/utils.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,11 @@ export function createReferenceItem(item, options = {}) {
8787
onclick={() => onToggleFile?.(item.uri)}
8888
>
8989
<span className="icon chevron keyboard_arrow_down" />
90-
<span className={`${iconClass} file-icon`} />
90+
<span
91+
className={`${iconClass} file-icon`}
92+
data-file-icon-name={item.fileName}
93+
data-file-icon-extra="file-icon"
94+
/>
9195
<span className="file-name">{sanitize(item.fileName)}</span>
9296
<span className="ref-count">{item.count}</span>
9397
</div>

src/components/settingsPage.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import fileIcons from "lib/fileIcons";
12
import "./settingsPage.scss";
23
import colorPicker from "dialogs/color";
34
import prompt from "dialogs/prompt";
@@ -390,12 +391,19 @@ function createListItemElement(item, options, useInfoAsDescription) {
390391
const $item = (
391392
<div
392393
tabIndex={1}
393-
className={`list-item ${item.sake ? "sake" : ""} ${item.icon || item.image ? "" : "no-leading-icon"}`}
394+
className={`list-item ${item.sake ? "sake" : ""} ${item.icon || item.image || item.fileIcon ? "" : "no-leading-icon"}`}
394395
data-key={item.key}
395396
data-action="list-item"
396397
>
397398
<span
398-
className={`icon ${item.icon || (item.image ? "" : "no-icon")}`}
399+
className={
400+
item.fileIcon
401+
? `icon ${fileIcons.icon(item.fileIcon)}`
402+
: `icon ${item.icon || (item.image ? "" : "no-icon")}`
403+
}
404+
data-file-icon-name={item.fileIcon?.name}
405+
data-file-icon-kind={item.fileIcon?.kind}
406+
data-file-icon-extra={item.fileIcon ? "icon" : undefined}
399407
style={{ color: item.iconColor }}
400408
>
401409
{item.image && (

src/dialogs/select.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Checkbox from "components/checkbox";
22
import tile from "components/tile";
33
import DOMPurify from "dompurify";
44
import actionStack from "lib/actionStack";
5+
import fileIcons from "lib/fileIcons";
56
import restoreTheme from "lib/restoreTheme";
67

78
/**
@@ -20,6 +21,7 @@ import restoreTheme from "lib/restoreTheme";
2021
* @property {string} [text]
2122
* @property {string} [subText]
2223
* @property {string} [icon]
24+
* @property {{name: string, kind?: "file" | "folder"}} [fileIcon]
2325
* @property {string} [className]
2426
* @property {string} [title]
2527
* @property {boolean} [disabled]
@@ -100,8 +102,18 @@ function select(title, items, options = {}) {
100102
itemOptions.text = item;
101103
}
102104

103-
// handle icon (lead)
104-
if (itemOptions.icon) {
105+
// File resources stay refreshable while image assets load.
106+
if (itemOptions.fileIcon) {
107+
const resource = itemOptions.fileIcon;
108+
lead = (
109+
<i
110+
className={`icon ${fileIcons.icon(resource)}`}
111+
data-file-icon-extra="icon"
112+
data-file-icon-name={resource.name}
113+
data-file-icon-kind={resource.kind || "file"}
114+
/>
115+
);
116+
} else if (itemOptions.icon) {
105117
if (itemOptions.icon === "letters" && !!itemOptions.letters) {
106118
lead = (
107119
<i className="icon letters" data-letters={itemOptions.letters}></i>

src/lang/ar-ye.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,5 +881,8 @@
881881
"wrap-indent-same": "Same",
882882
"wrap-indent-indent": "Indent (+1 level)",
883883
"wrap-indent-deep": "Deep indent (+2 levels)",
884-
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size."
884+
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size.",
885+
"icon pack": "Icon pack",
886+
"settings-info-icon-pack": "Choose the icons used for files and folders in the explorer and file lists across the app.",
887+
"unavailable": "unavailable"
885888
}

src/lang/be-by.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,5 +881,8 @@
881881
"wrap-indent-same": "Same",
882882
"wrap-indent-indent": "Indent (+1 level)",
883883
"wrap-indent-deep": "Deep indent (+2 levels)",
884-
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size."
884+
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size.",
885+
"icon pack": "Icon pack",
886+
"settings-info-icon-pack": "Choose the icons used for files and folders in the explorer and file lists across the app.",
887+
"unavailable": "unavailable"
885888
}

src/lang/bn-bd.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,5 +881,8 @@
881881
"wrap-indent-same": "Same",
882882
"wrap-indent-indent": "Indent (+1 level)",
883883
"wrap-indent-deep": "Deep indent (+2 levels)",
884-
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size."
884+
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size.",
885+
"icon pack": "Icon pack",
886+
"settings-info-icon-pack": "Choose the icons used for files and folders in the explorer and file lists across the app.",
887+
"unavailable": "unavailable"
885888
}

src/lang/cs-cz.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,5 +881,8 @@
881881
"wrap-indent-same": "Same",
882882
"wrap-indent-indent": "Indent (+1 level)",
883883
"wrap-indent-deep": "Deep indent (+2 levels)",
884-
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size."
884+
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size.",
885+
"icon pack": "Icon pack",
886+
"settings-info-icon-pack": "Choose the icons used for files and folders in the explorer and file lists across the app.",
887+
"unavailable": "unavailable"
885888
}

src/lang/de-de.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,5 +881,8 @@
881881
"wrap-indent-same": "Same",
882882
"wrap-indent-indent": "Indent (+1 level)",
883883
"wrap-indent-deep": "Deep indent (+2 levels)",
884-
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size."
884+
"settings-info-editor-wrapping-indent": "Choose whether wrapped text starts at the left edge, matches the original line's indentation, or is indented further. Each extra level uses your tab size.",
885+
"icon pack": "Icon pack",
886+
"settings-info-icon-pack": "Choose the icons used for files and folders in the explorer and file lists across the app.",
887+
"unavailable": "unavailable"
885888
}

src/lang/en-us.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@
179179
"light": "Light",
180180
"dark": "Dark",
181181
"file browser": "File Browser",
182+
"icon pack": "Icon pack",
183+
"settings-info-icon-pack": "Choose the icons used for files and folders in the explorer and file lists across the app.",
184+
"unavailable": "unavailable",
182185
"operation not permitted": "Operation not permitted",
183186
"no such file or directory": "No such file or directory",
184187
"input/output error": "Input/output error",

0 commit comments

Comments
 (0)