diff --git a/.prettierignore b/.prettierignore
deleted file mode 100644
index 5d4fe03a..00000000
--- a/.prettierignore
+++ /dev/null
@@ -1,6 +0,0 @@
-build
-dist
-out
-node_modules
-*.min.
-packages/**/*.ejs
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
deleted file mode 100644
index e1525349..00000000
--- a/.prettierrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "tabWidth": 4,
- "useTabs": false,
- "printWidth": 150
-}
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index d17b7472..0ad22a49 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -1,7 +1,7 @@
{
"recommendations": [
"kimlimjustin.jsdoc-generator",
- "esbenp.prettier-vscode",
- "orta.vscode-jest"
+ "orta.vscode-jest",
+ "biomejs.biome"
]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index a795ea8d..2b478d5f 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,11 +1,12 @@
{
- "eslint.format.enable": true,
- "editor.codeActionsOnSave": {
- "source.fixAll.eslint": "explicit"
- },
- "editor.defaultFormatter": "esbenp.prettier-vscode",
- "editor.formatOnSave": true,
- "[rust]": {
- "editor.defaultFormatter": "rust-lang.rust"
- }
+ "eslint.format.enable": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit",
+ "source.organizeImports.biome": "explicit"
+ },
+ "editor.defaultFormatter": "biomejs.biome",
+ "editor.formatOnSave": true,
+ "[rust]": {
+ "editor.defaultFormatter": "rust-lang.rust"
+ }
}
diff --git a/biome.json b/biome.json
new file mode 100644
index 00000000..735abe7e
--- /dev/null
+++ b/biome.json
@@ -0,0 +1,39 @@
+{
+ "$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
+ "files": {
+ "ignore": ["node_modules", "out", "src-tauri", "build", "dist", "*.min.js"]
+ },
+ "organizeImports": {
+ "enabled": true
+ },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true,
+ "style": {
+ "useDefaultParameterLast": "warn",
+ "noParameterAssign": "warn"
+ },
+ "complexity": {
+ "noForEach": "warn"
+ },
+ "suspicious": {
+ "noExplicitAny": "warn",
+ "noAssignInExpressions": "warn"
+ },
+ "a11y": {
+ "useValidAnchor": "warn"
+ },
+ "performance": {
+ "noAccumulatingSpread": "warn",
+ "noDelete": "warn"
+ }
+ }
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "space",
+ "indentWidth": 4,
+ "lineWidth": 150
+ }
+}
diff --git a/package.json b/package.json
index 34df911e..e4909137 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,8 @@
"postcrowdin:pull": "node scripts/post_crowdin_pull.js",
"crowdin:sync": "yarn --cwd ./docs write-translations && crowdin upload && crowdin download",
"lint": "eslint -c .eslintrc.yml --ext .ts ./src",
- "prettier": "prettier --write src",
+ "lint:biome": "biome lint src",
+ "format": "biome format --write src",
"grunt": "grunt",
"css:minify": "cleancss --batch --batch-suffix \"\" out/**/*.css ",
"prebuild": "yarn compile && yarn grunt && yarn css:minify",
@@ -55,6 +56,7 @@
],
"license": "Apache-2.0",
"devDependencies": {
+ "@biomejs/biome": "1.6.4",
"@crowdin/cli": "^3.6.5",
"@tauri-apps/cli": "^2.0.0-beta.14",
"@types/jest": "^29.5.12",
@@ -87,7 +89,6 @@
"node-watch": "^0.7.1",
"patch-package": "^8.0.0",
"postinstall-postinstall": "^2.1.0",
- "prettier": "3.2.5",
"rimraf": "^5.0.5",
"sass": "^1.63.4",
"sass-loader": "^14.1.1",
diff --git a/src/App.tsx b/src/App.tsx
index 681f437d..1a0756a2 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -6,16 +6,16 @@ import SettingsView from "./Components/SettingsView";
import ContextMenu from "./Components/ContextMenu";
import Header from "./Components/Header";
+import Infobar from "./Components/Infobar";
import LoadingBar from "./Components/LoadingBar";
+import OperationBar from "./Components/OperationBar";
import Properties from "./Components/Properties";
import Sidebar from "./Components/Sidebar";
-import Infobar from "./Components/Infobar";
-import OperationBar from "./Components/OperationBar";
-import { setActiveTab } from "./Store/ActionCreators/TabActionCreators";
-import { IAppState } from "./Store/Reducers";
-import "./Public/style.scss";
import { ThemedDiv } from "./Components/Theme";
+import "./Public/style.scss";
+import { setActiveTab } from "./Store/ActionCreators/TabActionCreators";
+import type { IAppState } from "./Store/Reducers";
const App = () => {
const dispatch = useDispatch();
@@ -26,15 +26,13 @@ const App = () => {
const [isLoaded, setIsLoaded] = useState(false); // TODO REPLACE WITH SKELETON LOADING
- const setCurrentDirectory = (path: string) => dispatch(setActiveTab({ ...activeTab, path }));
-
// Waits for homeDirectory to load on initial favorites request
useEffect(() => {
- if (homeDirectory) {
+ if (homeDirectory && activeTab.path !== homeDirectory) {
setIsLoaded(true);
- setCurrentDirectory(homeDirectory);
+ dispatch(setActiveTab({ ...activeTab, path: homeDirectory }));
}
- }, [homeDirectory]);
+ }, [homeDirectory, dispatch, activeTab]);
if (!isLoaded) return
Loading...
;
diff --git a/src/Components/ContextMenu/configs/bodyMenu.config.ts b/src/Components/ContextMenu/configs/bodyMenu.config.ts
index 74647fa2..17e1c2a8 100644
--- a/src/Components/ContextMenu/configs/bodyMenu.config.ts
+++ b/src/Components/ContextMenu/configs/bodyMenu.config.ts
@@ -1,217 +1,225 @@
-import contextMenuItem from '../../../Typings/contextMenuItem';
-import Storage from '../../../Service/storage';
-import { reload } from '../../Layout/windowManager';
-import Paste from '../../Files/File Operation/paste';
-import Undo from '../../Files/File Operation/undo';
-import Redo from '../../Files/File Operation/redo';
-import copyLocation from '../../Files/File Operation/location';
-import Pin from '../../Files/File Operation/pin';
-import Properties from '../../Properties/properties';
-import focusingPath from '../../Functions/focusingPath';
-import reveal from '../../../Service/reveal';
-import Translate from '../../I18n/i18n';
-import New from '../../Functions/new';
-import { isVSCodeInstalled } from '../../../Service/app';
-import { Purge, Restore } from '../../Files/File Operation/trash';
+import { isVSCodeInstalled } from "../../../Service/app";
+import reveal from "../../../Service/reveal";
+import Storage from "../../../Service/storage";
+import type contextMenuItem from "../../../Typings/contextMenuItem";
+import copyLocation from "../../Files/File Operation/location";
+import Paste from "../../Files/File Operation/paste";
+import Pin from "../../Files/File Operation/pin";
+import Redo from "../../Files/File Operation/redo";
+import { Purge, Restore } from "../../Files/File Operation/trash";
+import Undo from "../../Files/File Operation/undo";
+import focusingPath from "../../Functions/focusingPath";
+import New from "../../Functions/new";
+import Translate from "../../I18n/i18n";
+import { reload } from "../../Layout/windowManager";
+import Properties from "../../Properties/properties";
interface Favorites {
- name: string;
- path: string;
+ name: string;
+ path: string;
}
const BodyMenu = async (target: HTMLElement, filePath: string): Promise => {
- const favorites: Favorites[] = (await Storage.get('sidebar'))?.favorites;
- const isPinned = !!favorites?.filter((favorite) => favorite.path === filePath).length ?? false;
- const _focusingPath = await focusingPath();
+ const favorites: Favorites[] = (await Storage.get("sidebar"))?.favorites;
+ const isPinned = !!favorites?.filter((favorite) => favorite.path === filePath).length ?? false;
+ const _focusingPath = await focusingPath();
- const changeLayout = async (selectedLayout: 'l' | 'm' | 's' | 'd') => {
- const layout = (await Storage.get('layout')) ?? {};
- layout[_focusingPath] = selectedLayout;
- Storage.set('layout', layout);
- reload();
- };
- const changeSortMethod = async (selectedMethod: 'A' | 'Z' | 'L' | 'F' | 'S' | 'T') => {
- const sort = (await Storage.get('layout')) ?? {};
- sort[_focusingPath] = selectedMethod;
- Storage.set('sort', sort);
- reload();
- };
- return [
- [
- {
- menu: await Translate('Layout Mode'),
- submenu: [
- {
- name: await Translate('Grid View (Large)'),
- role: () => changeLayout('l'),
- icon: 'grid_large',
- },
- {
- name: await Translate('Grid View (Medium)'),
- role: () => changeLayout('m'),
- icon: 'grid_medium',
- },
- {
- name: await Translate('Grid View (Small)'),
- role: () => changeLayout('s'),
- icon: 'grid_small',
- },
- {
- name: await Translate('Detail View'),
- role: () => changeLayout('d'),
- icon: 'detail',
- },
- ],
- icon: 'layout',
- },
- {
- menu: await Translate('Sort By'),
- submenu: [
- {
- name: await Translate('A-Z'),
- role: () => changeSortMethod('A'),
- },
- {
- name: await Translate('Z-A'),
- role: () => changeSortMethod('Z'),
- },
- {
- name: await Translate('Last Modified'),
- role: () => changeSortMethod('L'),
- },
- {
- name: await Translate('First Modified'),
- role: () => changeSortMethod('F'),
- },
- {
- name: await Translate('Size'),
- role: () => changeSortMethod('S'),
- },
- {
- name: await Translate('Type'),
- role: () => changeSortMethod('T'),
- },
- ],
- icon: 'sort',
- },
- {
- menu: await Translate('Reload'),
- role: () => {
- reload();
- },
- shortcut: 'F5',
- icon: 'refresh',
- },
- ],
- [
- {
- menu: await Translate('Paste'),
- visible: !_focusingPath.startsWith('xplorer://'),
- shortcut: 'Ctrl+V',
- icon: 'paste',
- role: () => {
- Paste(filePath);
- },
- },
- {
- menu: await Translate('Undo Action'),
- visible: !_focusingPath.startsWith('xplorer://'),
- shortcut: 'Ctrl+Z',
- icon: 'undo',
- role: () => {
- Undo();
- },
- },
- {
- menu: await Translate('Redo Action'),
- visible: !_focusingPath.startsWith('xplorer://'),
- shortcut: 'Ctrl+Y',
- icon: 'redo',
- role: () => {
- Redo();
- },
- },
- {
- menu: await Translate('Copy Location Path'),
- shortcut: 'Alt+Shift+C',
- icon: 'location',
- role: () => {
- copyLocation(target);
- },
- },
- {
- menu: await Translate('Clear Recent List'),
- icon: 'delete',
- visible: _focusingPath === 'xplorer://Recent',
- role: () => {
- Storage.set('recent', []);
- reload();
- },
- },
- ],
- [
- {
- menu: await Translate('Open in Terminal'),
- visible: !_focusingPath.startsWith('xplorer://'),
- shortcut: 'Alt+T',
- icon: 'terminal',
- role: () => reveal(filePath, 'terminal'),
- },
- {
- menu: await Translate('Open in VSCode'),
- visible: (await isVSCodeInstalled()) && !_focusingPath.startsWith('xplorer://'),
- shortcut: 'Shift+Enter',
- icon: 'vscode',
- role: () => {
- reveal(filePath, 'vscode');
- },
- },
- {
- menu: await Translate('Restore all files'),
- visible: _focusingPath === 'xplorer://Trash',
- icon: 'delete',
- role: () => {
- const filePaths = [...document.querySelectorAll('.file')].map((file) => decodeURI(file.dataset.path));
- Restore(filePaths);
- },
- },
- {
- menu: await Translate('Permanently delete all files'),
- visible: _focusingPath === 'xplorer://Trash',
- icon: 'delete',
- role: () => {
- const filePaths = [...document.querySelectorAll('.file')].map((file) => decodeURI(file.dataset.path));
- Purge(filePaths);
- },
- },
- {
- menu: await Translate('New'),
- visible: !_focusingPath.startsWith('xplorer://'),
- submenu: [
- { name: await Translate('Folder'), shortcut: 'Shift+N', role: () => New('folder') },
- { name: await Translate('File'), shortcut: 'Alt+N', role: () => New('file') },
- ],
- icon: 'new',
- },
- ],
- [
- {
- menu: await Translate(isPinned ? 'Unpin from Sidebar' : 'Pin to Sidebar'),
- shortcut: 'Alt+P',
- icon: 'pin',
- role: () => {
- Pin([filePath]);
- },
- },
- {
- menu: await Translate('Properties'),
- shortcut: 'Ctrl+P',
- icon: target?.dataset?.isdir ? 'folder setting' : 'file setting',
- role: () => {
- Properties(filePath);
- },
- },
- ],
- ];
+ const changeLayout = async (selectedLayout: "l" | "m" | "s" | "d") => {
+ const layout = (await Storage.get("layout")) ?? {};
+ layout[_focusingPath] = selectedLayout;
+ Storage.set("layout", layout);
+ reload();
+ };
+ const changeSortMethod = async (selectedMethod: "A" | "Z" | "L" | "F" | "S" | "T") => {
+ const sort = (await Storage.get("layout")) ?? {};
+ sort[_focusingPath] = selectedMethod;
+ Storage.set("sort", sort);
+ reload();
+ };
+ return [
+ [
+ {
+ menu: await Translate("Layout Mode"),
+ submenu: [
+ {
+ name: await Translate("Grid View (Large)"),
+ role: () => changeLayout("l"),
+ icon: "grid_large",
+ },
+ {
+ name: await Translate("Grid View (Medium)"),
+ role: () => changeLayout("m"),
+ icon: "grid_medium",
+ },
+ {
+ name: await Translate("Grid View (Small)"),
+ role: () => changeLayout("s"),
+ icon: "grid_small",
+ },
+ {
+ name: await Translate("Detail View"),
+ role: () => changeLayout("d"),
+ icon: "detail",
+ },
+ ],
+ icon: "layout",
+ },
+ {
+ menu: await Translate("Sort By"),
+ submenu: [
+ {
+ name: await Translate("A-Z"),
+ role: () => changeSortMethod("A"),
+ },
+ {
+ name: await Translate("Z-A"),
+ role: () => changeSortMethod("Z"),
+ },
+ {
+ name: await Translate("Last Modified"),
+ role: () => changeSortMethod("L"),
+ },
+ {
+ name: await Translate("First Modified"),
+ role: () => changeSortMethod("F"),
+ },
+ {
+ name: await Translate("Size"),
+ role: () => changeSortMethod("S"),
+ },
+ {
+ name: await Translate("Type"),
+ role: () => changeSortMethod("T"),
+ },
+ ],
+ icon: "sort",
+ },
+ {
+ menu: await Translate("Reload"),
+ role: () => {
+ reload();
+ },
+ shortcut: "F5",
+ icon: "refresh",
+ },
+ ],
+ [
+ {
+ menu: await Translate("Paste"),
+ visible: !_focusingPath.startsWith("xplorer://"),
+ shortcut: "Ctrl+V",
+ icon: "paste",
+ role: () => {
+ Paste(filePath);
+ },
+ },
+ {
+ menu: await Translate("Undo Action"),
+ visible: !_focusingPath.startsWith("xplorer://"),
+ shortcut: "Ctrl+Z",
+ icon: "undo",
+ role: () => {
+ Undo();
+ },
+ },
+ {
+ menu: await Translate("Redo Action"),
+ visible: !_focusingPath.startsWith("xplorer://"),
+ shortcut: "Ctrl+Y",
+ icon: "redo",
+ role: () => {
+ Redo();
+ },
+ },
+ {
+ menu: await Translate("Copy Location Path"),
+ shortcut: "Alt+Shift+C",
+ icon: "location",
+ role: () => {
+ copyLocation(target);
+ },
+ },
+ {
+ menu: await Translate("Clear Recent List"),
+ icon: "delete",
+ visible: _focusingPath === "xplorer://Recent",
+ role: () => {
+ Storage.set("recent", []);
+ reload();
+ },
+ },
+ ],
+ [
+ {
+ menu: await Translate("Open in Terminal"),
+ visible: !_focusingPath.startsWith("xplorer://"),
+ shortcut: "Alt+T",
+ icon: "terminal",
+ role: () => reveal(filePath, "terminal"),
+ },
+ {
+ menu: await Translate("Open in VSCode"),
+ visible: (await isVSCodeInstalled()) && !_focusingPath.startsWith("xplorer://"),
+ shortcut: "Shift+Enter",
+ icon: "vscode",
+ role: () => {
+ reveal(filePath, "vscode");
+ },
+ },
+ {
+ menu: await Translate("Restore all files"),
+ visible: _focusingPath === "xplorer://Trash",
+ icon: "delete",
+ role: () => {
+ const filePaths = [...document.querySelectorAll(".file")].map((file) => decodeURI(file.dataset.path));
+ Restore(filePaths);
+ },
+ },
+ {
+ menu: await Translate("Permanently delete all files"),
+ visible: _focusingPath === "xplorer://Trash",
+ icon: "delete",
+ role: () => {
+ const filePaths = [...document.querySelectorAll(".file")].map((file) => decodeURI(file.dataset.path));
+ Purge(filePaths);
+ },
+ },
+ {
+ menu: await Translate("New"),
+ visible: !_focusingPath.startsWith("xplorer://"),
+ submenu: [
+ {
+ name: await Translate("Folder"),
+ shortcut: "Shift+N",
+ role: () => New("folder"),
+ },
+ {
+ name: await Translate("File"),
+ shortcut: "Alt+N",
+ role: () => New("file"),
+ },
+ ],
+ icon: "new",
+ },
+ ],
+ [
+ {
+ menu: await Translate(isPinned ? "Unpin from Sidebar" : "Pin to Sidebar"),
+ shortcut: "Alt+P",
+ icon: "pin",
+ role: () => {
+ Pin([filePath]);
+ },
+ },
+ {
+ menu: await Translate("Properties"),
+ shortcut: "Ctrl+P",
+ icon: target?.dataset?.isdir ? "folder setting" : "file setting",
+ role: () => {
+ Properties(filePath);
+ },
+ },
+ ],
+ ];
};
export default BodyMenu;
diff --git a/src/Components/ContextMenu/configs/fileMenu.config.ts b/src/Components/ContextMenu/configs/fileMenu.config.ts
index 20294d28..9774e188 100644
--- a/src/Components/ContextMenu/configs/fileMenu.config.ts
+++ b/src/Components/ContextMenu/configs/fileMenu.config.ts
@@ -1,159 +1,162 @@
-import Preview from '../../Files/File Preview/preview';
-import { isVSCodeInstalled } from '../../../Service/app';
-import contextMenuItem from '../../../Typings/contextMenuItem';
-import { createNewTab } from '../../Layout/tab';
-import reveal from '../../../Service/reveal';
-import Cut from '../../Files/File Operation/cut';
-import Copy from '../../Files/File Operation/copy';
-import copyLocation from '../../Files/File Operation/location';
-import Rename from '../../Files/File Operation/rename';
-import { Purge, Restore, Trash } from '../../Files/File Operation/trash';
-import Pin from '../../Files/File Operation/pin';
-import Properties from '../../Properties/properties';
-import focusingPath from '../../Functions/focusingPath';
-import Translate from '../../I18n/i18n';
-import { OpenDir } from '../../Open/open';
-import FileAPI from '../../../Service/files';
-import Storage from '../../../Service/storage';
-import Ask from '../../Prompt/ask';
+import { isVSCodeInstalled } from "../../../Service/app";
+import FileAPI from "../../../Service/files";
+import reveal from "../../../Service/reveal";
+import Storage from "../../../Service/storage";
+import type contextMenuItem from "../../../Typings/contextMenuItem";
+import Copy from "../../Files/File Operation/copy";
+import Cut from "../../Files/File Operation/cut";
+import copyLocation from "../../Files/File Operation/location";
+import Pin from "../../Files/File Operation/pin";
+import Rename from "../../Files/File Operation/rename";
+import { Purge, Restore, Trash } from "../../Files/File Operation/trash";
+import Preview from "../../Files/File Preview/preview";
+import focusingPath from "../../Functions/focusingPath";
+import Translate from "../../I18n/i18n";
+import { createNewTab } from "../../Layout/tab";
+import { OpenDir } from "../../Open/open";
+import Ask from "../../Prompt/ask";
+import Properties from "../../Properties/properties";
interface Favorites {
- name: string;
- path: string;
+ name: string;
+ path: string;
}
const FileMenu = async (target: HTMLElement, filePath: string): Promise => {
- const favorites: Favorites[] = (await Storage.get('sidebar'))?.favorites;
- const isPinned = !!favorites?.filter((favorite) => favorite.path === filePath).length ?? false;
- const _focusingPath = await focusingPath();
- return [
- [
- {
- menu: await Translate('Open'),
- shortcut: 'Enter',
- icon: 'open',
- role: () => {
- if (target.dataset.isdir !== 'true') {
- new FileAPI(filePath).openFile();
- } else OpenDir(filePath);
- },
- },
- {
- menu: await Translate('Open in New Tab'),
- visible: target?.dataset?.isdir === 'true',
- icon: 'open in new tab',
- role: () => {
- createNewTab(filePath);
- },
- },
- {
- menu: await Translate('Open in Terminal'),
- visible: target?.dataset?.isdir === 'true',
- shortcut: 'Alt+T',
- icon: 'terminal',
- role: () => {
- reveal(filePath, 'terminal');
- },
- },
- {
- menu: await Translate('Open in VSCode'),
- visible: await isVSCodeInstalled(),
- shortcut: 'Shift+Enter',
- icon: 'vscode',
- role: () => {
- reveal(filePath, 'vscode');
- },
- },
- {
- menu: await Translate('Preview'),
- visible: target?.dataset?.isdir !== 'true',
- shortcut: 'Ctrl+O',
- icon: 'preview',
- role: () => Preview(filePath),
- },
- ],
- [
- {
- menu: await Translate('Cut'),
- shortcut: 'Ctrl+X',
- icon: 'cut',
- role: () => Cut([filePath]),
- },
- {
- menu: await Translate('Copy'),
- shortcut: 'Ctrl+C',
- icon: 'copy',
- role: () => Copy([filePath]),
- },
- {
- menu: await Translate('Copy Location Path'),
- shortcut: 'Alt+Shift+C',
- icon: 'location',
- role: () => copyLocation(target),
- },
- ],
- [
- {
- menu: await Translate('Rename'),
- shortcut: 'F2',
- icon: 'rename',
- role: () => Rename(filePath),
- },
- {
- menu: await Translate('Delete'),
- visible: _focusingPath !== 'xplorer://Trash',
- shortcut: 'Del',
- icon: 'delete',
- role: () => Trash([filePath]),
- },
- {
- menu: await Translate('Restore'),
- icon: 'delete',
- visible: _focusingPath === 'xplorer://Trash',
- role: () => {
- Restore([decodeURI(filePath)]);
- },
- },
- {
- menu: await Translate('Permanently Delete'),
- icon: 'delete',
- visible: _focusingPath === 'xplorer://Trash',
- shortcut: 'Shift+Del',
- role: () => {
- Purge([decodeURI(filePath)]);
- },
- },
- {
- menu: await Translate(isPinned ? 'Unpin from Sidebar' : 'Pin to Sidebar'),
- shortcut: 'Alt+P',
- icon: 'pin',
- role: () => Pin([filePath]),
- },
- {
- menu: await Translate('Extract'),
- visible: target?.dataset?.path?.endsWith('.zip') ?? false,
- icon: 'unzip',
- role: async () => {
- const target = await Ask('Extract files', 'Extract files to', { value: filePath.replace('.zip', ''), selectFileName: false });
- new FileAPI(filePath).unzip(target);
- },
- },
- {
- menu: await Translate('Compress to Zip'),
- icon: 'zip',
- role: () => {
- new FileAPI(filePath).zip();
- },
- },
- ],
- [
- {
- menu: await Translate('Properties'),
- shortcut: 'Ctrl+P',
- icon: target?.dataset?.isdir ? 'folder setting' : 'file setting',
- role: () => Properties(filePath),
- },
- ],
- ];
+ const favorites: Favorites[] = (await Storage.get("sidebar"))?.favorites;
+ const isPinned = !!favorites?.filter((favorite) => favorite.path === filePath).length ?? false;
+ const _focusingPath = await focusingPath();
+ return [
+ [
+ {
+ menu: await Translate("Open"),
+ shortcut: "Enter",
+ icon: "open",
+ role: () => {
+ if (target.dataset.isdir !== "true") {
+ new FileAPI(filePath).openFile();
+ } else OpenDir(filePath);
+ },
+ },
+ {
+ menu: await Translate("Open in New Tab"),
+ visible: target?.dataset?.isdir === "true",
+ icon: "open in new tab",
+ role: () => {
+ createNewTab(filePath);
+ },
+ },
+ {
+ menu: await Translate("Open in Terminal"),
+ visible: target?.dataset?.isdir === "true",
+ shortcut: "Alt+T",
+ icon: "terminal",
+ role: () => {
+ reveal(filePath, "terminal");
+ },
+ },
+ {
+ menu: await Translate("Open in VSCode"),
+ visible: await isVSCodeInstalled(),
+ shortcut: "Shift+Enter",
+ icon: "vscode",
+ role: () => {
+ reveal(filePath, "vscode");
+ },
+ },
+ {
+ menu: await Translate("Preview"),
+ visible: target?.dataset?.isdir !== "true",
+ shortcut: "Ctrl+O",
+ icon: "preview",
+ role: () => Preview(filePath),
+ },
+ ],
+ [
+ {
+ menu: await Translate("Cut"),
+ shortcut: "Ctrl+X",
+ icon: "cut",
+ role: () => Cut([filePath]),
+ },
+ {
+ menu: await Translate("Copy"),
+ shortcut: "Ctrl+C",
+ icon: "copy",
+ role: () => Copy([filePath]),
+ },
+ {
+ menu: await Translate("Copy Location Path"),
+ shortcut: "Alt+Shift+C",
+ icon: "location",
+ role: () => copyLocation(target),
+ },
+ ],
+ [
+ {
+ menu: await Translate("Rename"),
+ shortcut: "F2",
+ icon: "rename",
+ role: () => Rename(filePath),
+ },
+ {
+ menu: await Translate("Delete"),
+ visible: _focusingPath !== "xplorer://Trash",
+ shortcut: "Del",
+ icon: "delete",
+ role: () => Trash([filePath]),
+ },
+ {
+ menu: await Translate("Restore"),
+ icon: "delete",
+ visible: _focusingPath === "xplorer://Trash",
+ role: () => {
+ Restore([decodeURI(filePath)]);
+ },
+ },
+ {
+ menu: await Translate("Permanently Delete"),
+ icon: "delete",
+ visible: _focusingPath === "xplorer://Trash",
+ shortcut: "Shift+Del",
+ role: () => {
+ Purge([decodeURI(filePath)]);
+ },
+ },
+ {
+ menu: await Translate(isPinned ? "Unpin from Sidebar" : "Pin to Sidebar"),
+ shortcut: "Alt+P",
+ icon: "pin",
+ role: () => Pin([filePath]),
+ },
+ {
+ menu: await Translate("Extract"),
+ visible: target?.dataset?.path?.endsWith(".zip") ?? false,
+ icon: "unzip",
+ role: async () => {
+ const target = await Ask("Extract files", "Extract files to", {
+ value: filePath.replace(".zip", ""),
+ selectFileName: false,
+ });
+ new FileAPI(filePath).unzip(target);
+ },
+ },
+ {
+ menu: await Translate("Compress to Zip"),
+ icon: "zip",
+ role: () => {
+ new FileAPI(filePath).zip();
+ },
+ },
+ ],
+ [
+ {
+ menu: await Translate("Properties"),
+ shortcut: "Ctrl+P",
+ icon: target?.dataset?.isdir ? "folder setting" : "file setting",
+ role: () => Properties(filePath),
+ },
+ ],
+ ];
};
export default FileMenu;
diff --git a/src/Components/ContextMenu/configs/multipleSelectedMenu.config.ts b/src/Components/ContextMenu/configs/multipleSelectedMenu.config.ts
index b2a30045..32dcab96 100644
--- a/src/Components/ContextMenu/configs/multipleSelectedMenu.config.ts
+++ b/src/Components/ContextMenu/configs/multipleSelectedMenu.config.ts
@@ -1,143 +1,143 @@
-import { getSelected } from '../../Files/File Operation/select';
-import contextMenuItem from '../../../Typings/contextMenuItem';
-import { isVSCodeInstalled } from '../../../Service/app';
-import { createNewTab } from '../../Layout/tab';
-import Cut from '../../Files/File Operation/cut';
-import Copy from '../../Files/File Operation/copy';
-import { Purge, Restore, Trash } from '../../Files/File Operation/trash';
-import Pin from '../../Files/File Operation/pin';
-import Translate from '../../I18n/i18n';
-import reveal from '../../../Service/reveal';
-import focusingPath from '../../Functions/focusingPath';
-import FileAPI from '../../../Service/files';
+import { isVSCodeInstalled } from "../../../Service/app";
+import FileAPI from "../../../Service/files";
+import reveal from "../../../Service/reveal";
+import type contextMenuItem from "../../../Typings/contextMenuItem";
+import Copy from "../../Files/File Operation/copy";
+import Cut from "../../Files/File Operation/cut";
+import Pin from "../../Files/File Operation/pin";
+import { getSelected } from "../../Files/File Operation/select";
+import { Purge, Restore, Trash } from "../../Files/File Operation/trash";
+import focusingPath from "../../Functions/focusingPath";
+import Translate from "../../I18n/i18n";
+import { createNewTab } from "../../Layout/tab";
const MultipleSelectedMenu = async (_: HTMLElement, _filePath: string): Promise => {
- const _focusingPath = await focusingPath();
- const selectedFiles = getSelected();
- const SELECTED_FILES_ARE_FILES = Array.from(selectedFiles).every((file) => file.dataset.isdir !== 'true');
- return [
- [
- {
- menu: await Translate('Open'),
- shortcut: 'Enter',
- icon: 'open',
- visible: SELECTED_FILES_ARE_FILES,
- role: () => {
- selectedFiles.forEach((file) => {
- new FileAPI(decodeURI(file.dataset.path)).openFile();
- });
- },
- },
- {
- menu: await Translate('Open in New Tab'),
- role: () => {
- for (const element of getSelected()) {
- if (element.dataset.isdir === 'true') {
- createNewTab(decodeURI(element.dataset.path));
- }
- }
- },
- icon: 'open in new tab',
- },
- {
- menu: await Translate('Open in VSCode'),
- role: () => {
- for (const selected of getSelected()) {
- const targetPath = decodeURI(selected.dataset.path) === 'undefined' ? _focusingPath : decodeURI(selected.dataset.path);
- reveal(targetPath, 'vscode');
- }
- },
- shortcut: 'Shift+Enter',
- visible: await isVSCodeInstalled(),
- icon: 'vscode',
- },
- ],
- [
- {
- menu: await Translate('Cut'),
- shortcut: 'Ctrl+X',
- icon: 'cut',
- role: () => {
- const paths = [];
- for (const element of getSelected()) {
- paths.push(decodeURI(element.dataset.path));
- }
- Cut(paths);
- },
- },
- {
- menu: await Translate('Copy'),
- shortcut: 'Ctrl+C',
- icon: 'copy',
- role: () => {
- const paths = [];
- for (const element of getSelected()) {
- paths.push(decodeURI(element.dataset.path));
- }
- Copy(paths);
- },
- },
- {
- menu: await Translate('Delete'),
- shortcut: 'Del',
- icon: 'delete',
- role: () => {
- const paths = [];
- for (const element of getSelected()) {
- paths.push(decodeURI(element.dataset.path));
- }
- Trash(paths);
- },
- },
- {
- menu: await Translate('Compress to Zip'),
- icon: 'zip',
- role: () => {
- const selectedFilesPath = [...selectedFiles].map((file) => decodeURI(file.dataset.path));
- new FileAPI(selectedFilesPath).zip();
- },
- },
- ],
- [
- {
- menu: await Translate('Restore these files'),
- visible: _focusingPath === 'xplorer://Trash',
- icon: 'delete',
- role: () => {
- const filePaths = [];
- for (const element of getSelected()) {
- filePaths.push(decodeURI(element.dataset.path));
- }
- Restore(filePaths);
- },
- },
- {
- menu: await Translate('Permanently delete these files'),
- visible: _focusingPath === 'xplorer://Trash',
- icon: 'delete',
- role: () => {
- const filePaths = [];
- for (const element of getSelected()) {
- filePaths.push(decodeURI(element.dataset.path));
- }
- Purge(filePaths);
- },
- },
- ],
- [
- {
- menu: await Translate('Pin to Sidebar'),
- shortcut: 'Alt+P',
- icon: 'pin',
- role: () => {
- const paths = [];
- for (const element of getSelected()) {
- paths.push(decodeURI(element.dataset.path));
- }
- Pin(paths);
- },
- },
- ],
- ];
+ const _focusingPath = await focusingPath();
+ const selectedFiles = getSelected();
+ const SELECTED_FILES_ARE_FILES = Array.from(selectedFiles).every((file) => file.dataset.isdir !== "true");
+ return [
+ [
+ {
+ menu: await Translate("Open"),
+ shortcut: "Enter",
+ icon: "open",
+ visible: SELECTED_FILES_ARE_FILES,
+ role: () => {
+ selectedFiles.forEach((file) => {
+ new FileAPI(decodeURI(file.dataset.path)).openFile();
+ });
+ },
+ },
+ {
+ menu: await Translate("Open in New Tab"),
+ role: () => {
+ for (const element of getSelected()) {
+ if (element.dataset.isdir === "true") {
+ createNewTab(decodeURI(element.dataset.path));
+ }
+ }
+ },
+ icon: "open in new tab",
+ },
+ {
+ menu: await Translate("Open in VSCode"),
+ role: () => {
+ for (const selected of getSelected()) {
+ const targetPath = decodeURI(selected.dataset.path) === "undefined" ? _focusingPath : decodeURI(selected.dataset.path);
+ reveal(targetPath, "vscode");
+ }
+ },
+ shortcut: "Shift+Enter",
+ visible: await isVSCodeInstalled(),
+ icon: "vscode",
+ },
+ ],
+ [
+ {
+ menu: await Translate("Cut"),
+ shortcut: "Ctrl+X",
+ icon: "cut",
+ role: () => {
+ const paths = [];
+ for (const element of getSelected()) {
+ paths.push(decodeURI(element.dataset.path));
+ }
+ Cut(paths);
+ },
+ },
+ {
+ menu: await Translate("Copy"),
+ shortcut: "Ctrl+C",
+ icon: "copy",
+ role: () => {
+ const paths = [];
+ for (const element of getSelected()) {
+ paths.push(decodeURI(element.dataset.path));
+ }
+ Copy(paths);
+ },
+ },
+ {
+ menu: await Translate("Delete"),
+ shortcut: "Del",
+ icon: "delete",
+ role: () => {
+ const paths = [];
+ for (const element of getSelected()) {
+ paths.push(decodeURI(element.dataset.path));
+ }
+ Trash(paths);
+ },
+ },
+ {
+ menu: await Translate("Compress to Zip"),
+ icon: "zip",
+ role: () => {
+ const selectedFilesPath = [...selectedFiles].map((file) => decodeURI(file.dataset.path));
+ new FileAPI(selectedFilesPath).zip();
+ },
+ },
+ ],
+ [
+ {
+ menu: await Translate("Restore these files"),
+ visible: _focusingPath === "xplorer://Trash",
+ icon: "delete",
+ role: () => {
+ const filePaths = [];
+ for (const element of getSelected()) {
+ filePaths.push(decodeURI(element.dataset.path));
+ }
+ Restore(filePaths);
+ },
+ },
+ {
+ menu: await Translate("Permanently delete these files"),
+ visible: _focusingPath === "xplorer://Trash",
+ icon: "delete",
+ role: () => {
+ const filePaths = [];
+ for (const element of getSelected()) {
+ filePaths.push(decodeURI(element.dataset.path));
+ }
+ Purge(filePaths);
+ },
+ },
+ ],
+ [
+ {
+ menu: await Translate("Pin to Sidebar"),
+ shortcut: "Alt+P",
+ icon: "pin",
+ role: () => {
+ const paths = [];
+ for (const element of getSelected()) {
+ paths.push(decodeURI(element.dataset.path));
+ }
+ Pin(paths);
+ },
+ },
+ ],
+ ];
};
export default MultipleSelectedMenu;
diff --git a/src/Components/ContextMenu/configs/sidebarDriveMenu.config.ts b/src/Components/ContextMenu/configs/sidebarDriveMenu.config.ts
index b401f011..1234bc34 100644
--- a/src/Components/ContextMenu/configs/sidebarDriveMenu.config.ts
+++ b/src/Components/ContextMenu/configs/sidebarDriveMenu.config.ts
@@ -1,29 +1,29 @@
-import { OpenDir } from '../../Open/open';
-import { createNewTab } from '../../Layout/tab';
-import contextMenuItem from '../../../Typings/contextMenuItem';
-import Translate from '../../I18n/i18n';
-import FileAPI from '../../../Service/files';
+import FileAPI from "../../../Service/files";
+import type contextMenuItem from "../../../Typings/contextMenuItem";
+import Translate from "../../I18n/i18n";
+import { createNewTab } from "../../Layout/tab";
+import { OpenDir } from "../../Open/open";
const SidebarDriveMenu = async (target: HTMLElement, filePath: string): Promise => {
- return [
- [
- {
- menu: await Translate('Open'),
- icon: 'open',
- role: () => {
- target?.dataset?.isdir === 'true' ? OpenDir(filePath) : new FileAPI(filePath).openFile();
- },
- },
- {
- menu: await Translate('Open in New Tab'),
- visible: target?.dataset?.isdir === 'true',
- icon: 'open in new tab',
- role: () => {
- createNewTab(filePath);
- },
- },
- ],
- ];
+ return [
+ [
+ {
+ menu: await Translate("Open"),
+ icon: "open",
+ role: () => {
+ target?.dataset?.isdir === "true" ? OpenDir(filePath) : new FileAPI(filePath).openFile();
+ },
+ },
+ {
+ menu: await Translate("Open in New Tab"),
+ visible: target?.dataset?.isdir === "true",
+ icon: "open in new tab",
+ role: () => {
+ createNewTab(filePath);
+ },
+ },
+ ],
+ ];
};
export default SidebarDriveMenu;
diff --git a/src/Components/ContextMenu/configs/sidebarMenu.config.ts b/src/Components/ContextMenu/configs/sidebarMenu.config.ts
index f8eec508..acbd0bf8 100644
--- a/src/Components/ContextMenu/configs/sidebarMenu.config.ts
+++ b/src/Components/ContextMenu/configs/sidebarMenu.config.ts
@@ -1,49 +1,49 @@
-import contextMenuItem from '../../../Typings/contextMenuItem';
-import { createNewTab } from '../../Layout/tab';
-import Pin from '../../Files/File Operation/pin';
-import Translate from '../../I18n/i18n';
-import Preview from '../../Files/File Preview/preview';
-import { OpenDir } from '../../Open/open';
-import FileAPI from '../../../Service/files';
+import FileAPI from "../../../Service/files";
+import type contextMenuItem from "../../../Typings/contextMenuItem";
+import Pin from "../../Files/File Operation/pin";
+import Preview from "../../Files/File Preview/preview";
+import Translate from "../../I18n/i18n";
+import { createNewTab } from "../../Layout/tab";
+import { OpenDir } from "../../Open/open";
const SidebarMenu = async (target: HTMLElement, filePath: string): Promise => {
- return [
- [
- {
- menu: await Translate('Open'),
- icon: 'open',
- role: () => {
- target?.dataset?.isdir === 'true' ? OpenDir(filePath) : new FileAPI(filePath).openFile();
- },
- },
- {
- menu: await Translate('Open in New Tab'),
- visible: target?.dataset?.isdir === 'true',
- icon: 'open in new tab',
- role: () => {
- createNewTab(filePath);
- },
- },
- {
- menu: await Translate('Preview'),
- shortcut: 'Ctrl+O',
- visible: target?.dataset?.isdir !== 'true',
- icon: 'preview',
- role: () => {
- Preview(filePath);
- },
- },
- ],
- [
- {
- menu: await Translate('Unpin from Sidebar'),
- icon: 'pin',
- role: () => {
- Pin([filePath]);
- },
- },
- ],
- ];
+ return [
+ [
+ {
+ menu: await Translate("Open"),
+ icon: "open",
+ role: () => {
+ target?.dataset?.isdir === "true" ? OpenDir(filePath) : new FileAPI(filePath).openFile();
+ },
+ },
+ {
+ menu: await Translate("Open in New Tab"),
+ visible: target?.dataset?.isdir === "true",
+ icon: "open in new tab",
+ role: () => {
+ createNewTab(filePath);
+ },
+ },
+ {
+ menu: await Translate("Preview"),
+ shortcut: "Ctrl+O",
+ visible: target?.dataset?.isdir !== "true",
+ icon: "preview",
+ role: () => {
+ Preview(filePath);
+ },
+ },
+ ],
+ [
+ {
+ menu: await Translate("Unpin from Sidebar"),
+ icon: "pin",
+ role: () => {
+ Pin([filePath]);
+ },
+ },
+ ],
+ ];
};
export default SidebarMenu;
diff --git a/src/Components/ContextMenu/contextMenu.ts b/src/Components/ContextMenu/contextMenu.ts
index 82ef5b49..0eb4ea92 100644
--- a/src/Components/ContextMenu/contextMenu.ts
+++ b/src/Components/ContextMenu/contextMenu.ts
@@ -1,103 +1,103 @@
-import SidebarMenu from './configs/sidebarMenu.config';
-import SidebarDriveMenu from './configs/sidebarDriveMenu.config';
-import BodyMenu from './configs/bodyMenu.config';
-import MultipleSelectedMenu from './configs/multipleSelectedMenu.config';
-import contextMenuItem from '../../Typings/contextMenuItem';
-import fileThumbnail from '../Thumbnail/thumbnail';
-import { getSelected } from '../Files/File Operation/select';
-import FileMenu from './configs/fileMenu.config';
-
-let contextMenu = document.querySelector('.contextmenu') as HTMLElement;
-let contextMenuSubmenus = document.getElementById('contextmenu-submenus');
+import type contextMenuItem from "../../Typings/contextMenuItem";
+import { getSelected } from "../Files/File Operation/select";
+import fileThumbnail from "../Thumbnail/thumbnail";
+import BodyMenu from "./configs/bodyMenu.config";
+import FileMenu from "./configs/fileMenu.config";
+import MultipleSelectedMenu from "./configs/multipleSelectedMenu.config";
+import SidebarDriveMenu from "./configs/sidebarDriveMenu.config";
+import SidebarMenu from "./configs/sidebarMenu.config";
+
+let contextMenu = document.querySelector(".contextmenu") as HTMLElement;
+let contextMenuSubmenus = document.getElementById("contextmenu-submenus");
interface menuRoles {
- [key: string]: () => void;
+ [key: string]: () => void;
}
const menuRoles: menuRoles = {};
-document.addEventListener('DOMContentLoaded', () => {
- contextMenu = document.querySelector('.contextmenu') as HTMLElement;
- contextMenuSubmenus = document.getElementById('contextmenu-submenus');
+document.addEventListener("DOMContentLoaded", () => {
+ contextMenu = document.querySelector(".contextmenu") as HTMLElement;
+ contextMenuSubmenus = document.getElementById("contextmenu-submenus");
});
const MenuToElements = async (menu: contextMenuItem[][]): Promise => {
- for (let index = 0; index < menu.length; index++) {
- const section = menu[index];
- for (let i = 0; i < section.length; i++) {
- const item = section[i];
- if (item.visible || item.visible === undefined) {
- const menu = document.createElement('span');
- menu.classList.add('contextmenu-item');
-
- if (item.icon) {
- if (item.shortcut)
- menu.innerHTML = `${item?.menu.trim()}`;
- else menu.innerHTML = `${item?.menu?.trim()}`;
- } else {
- if (item.shortcut) menu.innerHTML = `${item?.menu?.trim()}`;
- else menu.innerHTML = item?.menu?.trim();
- }
-
- if (typeof item?.role === 'function') {
- const roleIdentifier = Math.random().toString(36).substr(2, 10) + item?.menu?.replace(/\W/g, '')?.trim();
- menu.setAttribute('role', roleIdentifier);
- menuRoles[roleIdentifier] = item?.role;
- }
- contextMenu.appendChild(menu);
-
- const submenuId = Math.random().toString(36).substr(2, 10);
-
- // Create submenu element for context menu
- if (item.submenu) {
- const submenu = document.createElement('div');
- submenu.classList.add('contextmenu-submenu');
-
- menu.dataset.submenu = submenuId;
- submenu.id = submenuId;
-
- contextMenuSubmenus.appendChild(submenu);
- for (let j = 0; j < item.submenu.length; j++) {
- const submenuItem = item.submenu[j];
- const submenuItemElement = document.createElement('span');
- submenuItemElement.classList.add('contextmenu-item');
-
- if (submenuItem.icon) {
- if (submenuItem.shortcut)
- submenuItemElement.innerHTML = `${
- submenuItem.name ?? submenuItem
- }`;
- else
- submenuItemElement.innerHTML = `${submenuItem?.name?.trim()}`;
- } else {
- if (submenuItem.shortcut)
- submenuItemElement.innerHTML = `${submenuItem.name ?? submenuItem}`;
- else submenuItemElement.innerHTML = submenuItem.name;
- }
-
- if (typeof submenuItem?.role === 'function') {
- const roleIdentifier = Math.random().toString(36).substr(2, 10) + submenuItem?.name?.replace(/\W/g, '')?.trim();
- submenuItemElement.setAttribute('role', roleIdentifier);
- menuRoles[roleIdentifier] = submenuItem?.role;
- }
-
- submenu.appendChild(submenuItemElement);
- }
- }
- }
- }
- if (index !== menu.length - 1 && section.filter((menu) => menu.visible !== false).length > 0) contextMenu.innerHTML += `
`;
- }
- return;
+ for (let index = 0; index < menu.length; index++) {
+ const section = menu[index];
+ for (let i = 0; i < section.length; i++) {
+ const item = section[i];
+ if (item.visible || item.visible === undefined) {
+ const menu = document.createElement("span");
+ menu.classList.add("contextmenu-item");
+
+ if (item.icon) {
+ if (item.shortcut)
+ menu.innerHTML = `${item?.menu.trim()}`;
+ else menu.innerHTML = `${item?.menu?.trim()}`;
+ } else {
+ if (item.shortcut) menu.innerHTML = `${item?.menu?.trim()}`;
+ else menu.innerHTML = item?.menu?.trim();
+ }
+
+ if (typeof item?.role === "function") {
+ const roleIdentifier = Math.random().toString(36).substr(2, 10) + item?.menu?.replace(/\W/g, "")?.trim();
+ menu.setAttribute("role", roleIdentifier);
+ menuRoles[roleIdentifier] = item?.role;
+ }
+ contextMenu.appendChild(menu);
+
+ const submenuId = Math.random().toString(36).substr(2, 10);
+
+ // Create submenu element for context menu
+ if (item.submenu) {
+ const submenu = document.createElement("div");
+ submenu.classList.add("contextmenu-submenu");
+
+ menu.dataset.submenu = submenuId;
+ submenu.id = submenuId;
+
+ contextMenuSubmenus.appendChild(submenu);
+ for (let j = 0; j < item.submenu.length; j++) {
+ const submenuItem = item.submenu[j];
+ const submenuItemElement = document.createElement("span");
+ submenuItemElement.classList.add("contextmenu-item");
+
+ if (submenuItem.icon) {
+ if (submenuItem.shortcut)
+ submenuItemElement.innerHTML = `${
+ submenuItem.name ?? submenuItem
+ }`;
+ else
+ submenuItemElement.innerHTML = `${submenuItem?.name?.trim()}`;
+ } else {
+ if (submenuItem.shortcut)
+ submenuItemElement.innerHTML = `${submenuItem.name ?? submenuItem}`;
+ else submenuItemElement.innerHTML = submenuItem.name;
+ }
+
+ if (typeof submenuItem?.role === "function") {
+ const roleIdentifier = Math.random().toString(36).substr(2, 10) + submenuItem?.name?.replace(/\W/g, "")?.trim();
+ submenuItemElement.setAttribute("role", roleIdentifier);
+ menuRoles[roleIdentifier] = submenuItem?.role;
+ }
+
+ submenu.appendChild(submenuItemElement);
+ }
+ }
+ }
+ }
+ if (index !== menu.length - 1 && section.filter((menu) => menu.visible !== false).length > 0) contextMenu.innerHTML += "
";
+ }
+ return;
};
/**
@@ -105,131 +105,131 @@ const MenuToElements = async (menu: contextMenuItem[][]): Promise => {
* @returns {void}
*/
const ContextMenu = (): void => {
- document.addEventListener('contextmenu', async (e) => {
- e.preventDefault();
- document.querySelectorAll('.hover-preview').forEach((element) => {
- element.parentNode.removeChild(element);
- });
- contextMenu.innerHTML = '';
- contextMenu.style.height = 'initial';
- contextMenu.style.overflowY = 'initial';
- contextMenuSubmenus.innerHTML = '';
- let coorX = e.pageX;
- let coorY = e.pageY;
-
- let target = e.target as HTMLElement;
- if (target.classList.contains('workspace')) target = target.querySelector('.workspace-tab');
- else if (target.classList.contains('contextmenu')) {
- exitContextMenu();
- return;
- } else {
- while (target.dataset && !target.dataset.path) {
- target = target.parentNode as HTMLElement;
- }
- }
- if (!target?.dataset?.path && !target?.classList?.contains('workspace')) return;
-
- const filePath = decodeURI(target.dataset.path);
-
- // Create the context menu
- if (getSelected().length > 1) {
- await MenuToElements(await MultipleSelectedMenu(target, filePath));
- } else if (target.classList.contains('favorite-item')) {
- await MenuToElements(await SidebarMenu(target, filePath));
- } else if (target.classList.contains('drive-item')) {
- await MenuToElements(await SidebarDriveMenu(target, filePath));
- } else if (target.classList.contains('workspace-tab')) {
- await MenuToElements(await BodyMenu(target, filePath));
- } else {
- await MenuToElements(await FileMenu(target, filePath));
- }
-
- if (coorY + contextMenu.offsetHeight > window.innerHeight && coorY - contextMenu.offsetHeight > -50) {
- coorY -= contextMenu.offsetHeight;
- }
- if (coorX + contextMenu.offsetWidth > window.innerWidth) coorX = window.innerWidth - contextMenu.offsetWidth;
- if (contextMenu.offsetHeight + coorY > window.innerHeight) {
- contextMenu.style.height = `${
- window.innerHeight - coorY - parseInt(window.getComputedStyle(contextMenu).getPropertyValue('padding-top')) * 2
- }px`;
- contextMenu.style.overflowY = 'auto';
- }
-
- contextMenu.style.left = coorX + 'px';
- contextMenu.style.top = coorY + 'px';
- contextMenu.scrollTop = 0;
-
- document.addEventListener('click', exitContextMenu);
- });
- const exitContextMenu = () => {
- contextMenu.style.left = '-100vw';
- contextMenu.style.top = '-100vh';
- contextMenuSubmenus.innerHTML = '';
- document.removeEventListener('click', exitContextMenu);
- };
-
- // Submenu handler
- document.addEventListener('mousemove', (e) => {
- // Expand contextmenu
- if (
- (e.pageX >= contextMenu.offsetLeft + contextMenu.offsetWidth - 15 || e.pageX <= contextMenu.offsetLeft + 15) &&
- e.pageX < contextMenu.offsetLeft + contextMenu.offsetWidth + 100
- ) {
- return;
- }
-
- if (!((e.target as HTMLElement).parentNode as HTMLElement).className.startsWith('contextmenu-submenu')) {
- document.querySelectorAll('.contextmenu-submenu').forEach((submenu) => ((submenu as HTMLElement).style.display = 'none'));
- }
- if (
- (e.target as HTMLElement).dataset.submenu ||
- ((e.target as HTMLElement).parentNode as HTMLElement).className.startsWith('contextmenu-submenu')
- ) {
- const submenuElement = document.getElementById((e.target as HTMLElement).dataset.submenu);
- if (!submenuElement) return;
-
- const menuCoordinate = (e.target as HTMLElement).getBoundingClientRect();
-
- submenuElement.style.display = 'block';
- submenuElement.style.height = 'initial';
- submenuElement.style.overflowY = 'initial';
-
- let submenuCoorX = contextMenu.offsetLeft + contextMenu.offsetWidth;
- if (submenuCoorX + submenuElement.offsetWidth * 0.5 >= window.innerWidth) {
- submenuCoorX = contextMenu.offsetLeft - submenuElement.offsetWidth;
- }
- if (submenuElement.offsetHeight + menuCoordinate.top > window.innerHeight) {
- submenuElement.style.height = `${
- window.innerHeight -
- submenuElement.offsetHeight -
- parseInt(window.getComputedStyle(submenuElement).getPropertyValue('padding-top')) * 2
- }px`;
- submenuElement.style.overflowY = 'auto';
- }
- submenuElement.style.left = submenuCoorX + 'px';
- submenuElement.style.top = menuCoordinate.top + 'px';
- submenuElement.scrollTop = 0;
- }
- });
-
- contextMenu.addEventListener('click', (e) => {
- exitContextMenu();
-
- const menuClicked = e.target as HTMLElement;
- const menuRole = menuClicked?.getAttribute('role');
- if (!menuRole) return;
-
- menuRoles[menuRole]();
- });
- contextMenuSubmenus.addEventListener('click', (e) => {
- exitContextMenu();
-
- const menuClicked = e.target as HTMLElement;
- const menuRole = menuClicked?.getAttribute('role');
- if (!menuRole) return;
-
- menuRoles[menuRole]();
- });
+ document.addEventListener("contextmenu", async (e) => {
+ e.preventDefault();
+ document.querySelectorAll(".hover-preview").forEach((element) => {
+ element.parentNode.removeChild(element);
+ });
+ contextMenu.innerHTML = "";
+ contextMenu.style.height = "initial";
+ contextMenu.style.overflowY = "initial";
+ contextMenuSubmenus.innerHTML = "";
+ let coorX = e.pageX;
+ let coorY = e.pageY;
+
+ let target = e.target as HTMLElement;
+ if (target.classList.contains("workspace")) target = target.querySelector(".workspace-tab");
+ else if (target.classList.contains("contextmenu")) {
+ exitContextMenu();
+ return;
+ } else {
+ while (target.dataset && !target.dataset.path) {
+ target = target.parentNode as HTMLElement;
+ }
+ }
+ if (!target?.dataset?.path && !target?.classList?.contains("workspace")) return;
+
+ const filePath = decodeURI(target.dataset.path);
+
+ // Create the context menu
+ if (getSelected().length > 1) {
+ await MenuToElements(await MultipleSelectedMenu(target, filePath));
+ } else if (target.classList.contains("favorite-item")) {
+ await MenuToElements(await SidebarMenu(target, filePath));
+ } else if (target.classList.contains("drive-item")) {
+ await MenuToElements(await SidebarDriveMenu(target, filePath));
+ } else if (target.classList.contains("workspace-tab")) {
+ await MenuToElements(await BodyMenu(target, filePath));
+ } else {
+ await MenuToElements(await FileMenu(target, filePath));
+ }
+
+ if (coorY + contextMenu.offsetHeight > window.innerHeight && coorY - contextMenu.offsetHeight > -50) {
+ coorY -= contextMenu.offsetHeight;
+ }
+ if (coorX + contextMenu.offsetWidth > window.innerWidth) coorX = window.innerWidth - contextMenu.offsetWidth;
+ if (contextMenu.offsetHeight + coorY > window.innerHeight) {
+ contextMenu.style.height = `${
+ window.innerHeight - coorY - Number.parseInt(window.getComputedStyle(contextMenu).getPropertyValue("padding-top")) * 2
+ }px`;
+ contextMenu.style.overflowY = "auto";
+ }
+
+ contextMenu.style.left = `${coorX}px`;
+ contextMenu.style.top = `${coorY}px`;
+ contextMenu.scrollTop = 0;
+
+ document.addEventListener("click", exitContextMenu);
+ });
+ const exitContextMenu = () => {
+ contextMenu.style.left = "-100vw";
+ contextMenu.style.top = "-100vh";
+ contextMenuSubmenus.innerHTML = "";
+ document.removeEventListener("click", exitContextMenu);
+ };
+
+ // Submenu handler
+ document.addEventListener("mousemove", (e) => {
+ // Expand contextmenu
+ if (
+ (e.pageX >= contextMenu.offsetLeft + contextMenu.offsetWidth - 15 || e.pageX <= contextMenu.offsetLeft + 15) &&
+ e.pageX < contextMenu.offsetLeft + contextMenu.offsetWidth + 100
+ ) {
+ return;
+ }
+
+ if (!((e.target as HTMLElement).parentNode as HTMLElement).className.startsWith("contextmenu-submenu")) {
+ document.querySelectorAll(".contextmenu-submenu").forEach((submenu) => ((submenu as HTMLElement).style.display = "none"));
+ }
+ if (
+ (e.target as HTMLElement).dataset.submenu ||
+ ((e.target as HTMLElement).parentNode as HTMLElement).className.startsWith("contextmenu-submenu")
+ ) {
+ const submenuElement = document.getElementById((e.target as HTMLElement).dataset.submenu);
+ if (!submenuElement) return;
+
+ const menuCoordinate = (e.target as HTMLElement).getBoundingClientRect();
+
+ submenuElement.style.display = "block";
+ submenuElement.style.height = "initial";
+ submenuElement.style.overflowY = "initial";
+
+ let submenuCoorX = contextMenu.offsetLeft + contextMenu.offsetWidth;
+ if (submenuCoorX + submenuElement.offsetWidth * 0.5 >= window.innerWidth) {
+ submenuCoorX = contextMenu.offsetLeft - submenuElement.offsetWidth;
+ }
+ if (submenuElement.offsetHeight + menuCoordinate.top > window.innerHeight) {
+ submenuElement.style.height = `${
+ window.innerHeight -
+ submenuElement.offsetHeight -
+ Number.parseInt(window.getComputedStyle(submenuElement).getPropertyValue("padding-top")) * 2
+ }px`;
+ submenuElement.style.overflowY = "auto";
+ }
+ submenuElement.style.left = `${submenuCoorX}px`;
+ submenuElement.style.top = `${menuCoordinate.top}px`;
+ submenuElement.scrollTop = 0;
+ }
+ });
+
+ contextMenu.addEventListener("click", (e) => {
+ exitContextMenu();
+
+ const menuClicked = e.target as HTMLElement;
+ const menuRole = menuClicked?.getAttribute("role");
+ if (!menuRole) return;
+
+ menuRoles[menuRole]();
+ });
+ contextMenuSubmenus.addEventListener("click", (e) => {
+ exitContextMenu();
+
+ const menuClicked = e.target as HTMLElement;
+ const menuRole = menuClicked?.getAttribute("role");
+ if (!menuRole) return;
+
+ menuRoles[menuRole]();
+ });
};
export default ContextMenu;
diff --git a/src/Components/ContextMenu/index.tsx b/src/Components/ContextMenu/index.tsx
index e8e015bf..c315015a 100644
--- a/src/Components/ContextMenu/index.tsx
+++ b/src/Components/ContextMenu/index.tsx
@@ -1,10 +1,10 @@
-import React from 'react';
+import React from "react";
const ContextMenu = () => (
- <>
-
-
- >
+ <>
+
+
+ >
);
export default ContextMenu;
diff --git a/src/Components/Drives/drives.ts b/src/Components/Drives/drives.ts
index 7942e607..59ad5e9d 100644
--- a/src/Components/Drives/drives.ts
+++ b/src/Components/Drives/drives.ts
@@ -1,21 +1,21 @@
-import formatBytes from '../Functions/filesize';
-import Translate from '../I18n/i18n';
-import fileThumbnail from '../Thumbnail/thumbnail';
-import { updateTheme } from '../Theme/theme';
-import DrivesAPI, { Drive } from '../../Service/drives';
-import OS from '../../Service/platform';
-import Storage from '../../Service/storage';
+import DrivesAPI, { type Drive } from "../../Service/drives";
+import OS from "../../Service/platform";
+import Storage from "../../Service/storage";
+import formatBytes from "../Functions/filesize";
+import Translate from "../I18n/i18n";
+import { updateTheme } from "../Theme/theme";
+import fileThumbnail from "../Thumbnail/thumbnail";
// Initialize values
let platform: string;
let driveInfo: DrivesAPI;
(async () => {
- if (!platform) {
- platform = await OS();
- }
- if (!driveInfo) {
- driveInfo = new DrivesAPI();
- driveInfo.build();
- }
+ if (!platform) {
+ platform = await OS();
+ }
+ if (!driveInfo) {
+ driveInfo = new DrivesAPI();
+ driveInfo.build();
+ }
})();
/**
@@ -24,27 +24,27 @@ let driveInfo: DrivesAPI;
* @returns {Promise} Result
*/
const drivesToElements = async (drives: Drive[]): Promise => {
- let result = drives.length ? `${await Translate(platform === 'linux' ? 'Pendrives' : 'Drives')}
` : ''; // Element Result
- for (const drive of drives) {
- const driveName = drive.mount_point.split('/')[drive.mount_point.split('/').length - 1]; // Get name of drive
- result += `
+ let result = drives.length ? `${await Translate(platform === "linux" ? "Pendrives" : "Drives")}
` : ""; // Element Result
+ for (const drive of drives) {
+ const driveName = drive.mount_point.split("/")[drive.mount_point.split("/").length - 1]; // Get name of drive
+ result += `
-
+
${
- drive.name ?? drive.disk_type
- ? `
${drive.name && /[^?]/.test(drive.name) ? drive.name : drive.disk_type} (${driveName})
` //eslint-disable-line
- : `
${driveName}
`
- }
-
-
${formatBytes(drive.available_space)} ${await Translate('free of')} ${formatBytes(drive.total_space)}
+ drive.name ?? drive.disk_type
+ ? `
${drive.name && /[^?]/.test(drive.name) ? drive.name : drive.disk_type} (${driveName})
` //eslint-disable-line
+ : `
${driveName}
`
+ }
+
+
${formatBytes(drive.available_space)} ${await Translate("free of")} ${formatBytes(drive.total_space)}
`;
- }
- return result;
+ }
+ return result;
};
/**
@@ -52,15 +52,14 @@ const drivesToElements = async (drives: Drive[]): Promise => {
* @returns {Promise} drive section HTML code
*/
const Drives = async (): Promise => {
- //if (focusingPath() === 'xplorer://Home') {
- switch (platform) {
- case 'darwin':
- return ''; // Xplorer does not support drives for macOS currently
- case 'win32':
- default:
- return `${await drivesToElements(driveInfo.DRIVES)}`;
- }
- //} else return '';
+ //if (focusingPath() === 'xplorer://Home') {
+ switch (platform) {
+ case "darwin":
+ return ""; // Xplorer does not support drives for macOS currently
+ default:
+ return `${await drivesToElements(driveInfo.DRIVES)}`;
+ }
+ //} else return '';
};
/**
@@ -68,40 +67,36 @@ const Drives = async (): Promise => {
* @returns {Promise}
*/
const writeSidebarDriveItems = async (): Promise => {
- const drives = driveInfo.DRIVES;
- const isWin32 = platform === 'win32';
- const driveElement = document.querySelector('#sidebar-drives');
- const driveBtnText = driveElement.querySelector('.sidebar-text');
- driveBtnText.textContent = await Translate(isWin32 ? 'Drives' : 'Pendrives');
- if (!drives.length || platform === 'darwin') {
- driveElement.hidden = true;
- return;
- }
- let content = '';
- for (const drive of drives) {
- let driveName: string;
- if (platform === 'win32') {
- const hasName = drive.name && /[^?]/.test(drive.name);
- driveName = hasName ? drive.name : drive.disk_type;
- driveName += ' (' + drive.mount_point.replace(/\\$/g, '') + ')';
- } else driveName = drive.mount_point.split('/').at(-1);
- const driveType = drive.is_removable ? 'usb' : 'hard-disk';
- const iconPath = await fileThumbnail(driveType, 'favorites', false);
- content +=
- ``;
- }
- const sidebar = await Storage.get('sidebar');
- const driveList = driveElement.querySelector('.sidebar-nav-list');
- driveList.innerHTML = content;
- if (sidebar?.hideSection?.drives) {
- const sidebarCollapseClass = 'sidebar-nav-dropdown-collapsed';
- driveElement.classList.add(sidebarCollapseClass);
- }
+ const drives = driveInfo.DRIVES;
+ const isWin32 = platform === "win32";
+ const driveElement = document.querySelector("#sidebar-drives");
+ const driveBtnText = driveElement.querySelector(".sidebar-text");
+ driveBtnText.textContent = await Translate(isWin32 ? "Drives" : "Pendrives");
+ if (!drives.length || platform === "darwin") {
+ driveElement.hidden = true;
+ return;
+ }
+ let content = "";
+ for (const drive of drives) {
+ let driveName: string;
+ if (platform === "win32") {
+ const hasName = drive.name && /[^?]/.test(drive.name);
+ driveName = hasName ? drive.name : drive.disk_type;
+ driveName += ` (${drive.mount_point.replace(/\\$/g, "")})`;
+ } else driveName = drive.mount_point.split("/").at(-1);
+ const driveType = drive.is_removable ? "usb" : "hard-disk";
+ const iconPath = await fileThumbnail(driveType, "favorites", false);
+ content += ``;
+ }
+ const sidebar = await Storage.get("sidebar");
+ const driveList = driveElement.querySelector(".sidebar-nav-list");
+ driveList.innerHTML = content;
+ if (sidebar?.hideSection?.drives) {
+ const sidebarCollapseClass = "sidebar-nav-dropdown-collapsed";
+ driveElement.classList.add(sidebarCollapseClass);
+ }
};
/**
@@ -109,21 +104,21 @@ const writeSidebarDriveItems = async (): Promise => {
* @returns {Promise}
*/
const detectDriveInit = async (): Promise => {
- if (!(await Storage.get('preference'))?.detectDriveChange) return;
- if (!driveInfo) {
- driveInfo = new DrivesAPI();
- await driveInfo.build();
- }
- driveInfo.detectChange(async () => {
- if (document.querySelector('.path-navigator').value === 'xplorer://Home') {
- const MAIN_DRIVES_ELEMENT = document.getElementById('drives');
- if (MAIN_DRIVES_ELEMENT.classList.contains('hidden')) MAIN_DRIVES_ELEMENT.classList.remove('hidden');
- const _driveSection = await Drives();
- MAIN_DRIVES_ELEMENT.innerHTML = _driveSection;
- }
- await writeSidebarDriveItems();
- updateTheme('favorites');
- });
+ if (!(await Storage.get("preference"))?.detectDriveChange) return;
+ if (!driveInfo) {
+ driveInfo = new DrivesAPI();
+ await driveInfo.build();
+ }
+ driveInfo.detectChange(async () => {
+ if (document.querySelector(".path-navigator").value === "xplorer://Home") {
+ const MAIN_DRIVES_ELEMENT = document.getElementById("drives");
+ if (MAIN_DRIVES_ELEMENT.classList.contains("hidden")) MAIN_DRIVES_ELEMENT.classList.remove("hidden");
+ const _driveSection = await Drives();
+ MAIN_DRIVES_ELEMENT.innerHTML = _driveSection;
+ }
+ await writeSidebarDriveItems();
+ updateTheme("favorites");
+ });
};
export { writeSidebarDriveItems, Drives, drivesToElements, detectDriveInit };
diff --git a/src/Components/Favorites/defaultFavorites.ts b/src/Components/Favorites/defaultFavorites.ts
index 5dcd7187..51e9292c 100644
--- a/src/Components/Favorites/defaultFavorites.ts
+++ b/src/Components/Favorites/defaultFavorites.ts
@@ -1,56 +1,56 @@
-import fileThumbnail from '../Thumbnail/thumbnail';
-import FavoritesAPI from '../../Service/favorites';
+import FavoritesAPI from "../../Service/favorites";
+import fileThumbnail from "../Thumbnail/thumbnail";
let FavoritesData: FavoritesAPI;
interface Favorites {
- name: string;
- path: string;
- icon: string;
+ name: string;
+ path: string;
+ icon: string;
}
export default async function defaultFavorites(): Promise {
- if (!FavoritesData) {
- FavoritesData = new FavoritesAPI();
- await FavoritesData.build();
- }
- return [
- {
- name: 'Recent',
- path: 'xplorer://Recent',
- icon: await fileThumbnail('recent', 'folder', false),
- },
- {
- name: 'Desktop',
- path: FavoritesData.DESKTOP_PATH,
- icon: await fileThumbnail('desktop', 'folder', false),
- },
- {
- name: 'Documents',
- path: FavoritesData.DOCUMENT_PATH,
- icon: await fileThumbnail('document', 'folder', false),
- },
- {
- name: 'Downloads',
- path: FavoritesData.DOWNLOAD_PATH,
- icon: await fileThumbnail('download', 'folder', false),
- },
- {
- name: 'Pictures',
- path: FavoritesData.PICTURE_PATH,
- icon: await fileThumbnail('picture', 'folder', false),
- },
- {
- name: 'Music',
- path: FavoritesData.MUSIC_PATH,
- icon: await fileThumbnail('music', 'folder', false),
- },
- {
- name: 'Videos',
- path: FavoritesData.VIDEO_PATH,
- icon: await fileThumbnail('video', 'folder', false),
- },
- {
- name: 'Trash',
- path: 'xplorer://Trash',
- icon: await fileThumbnail('trash', 'folder', false),
- },
- ];
+ if (!FavoritesData) {
+ FavoritesData = new FavoritesAPI();
+ await FavoritesData.build();
+ }
+ return [
+ {
+ name: "Recent",
+ path: "xplorer://Recent",
+ icon: await fileThumbnail("recent", "folder", false),
+ },
+ {
+ name: "Desktop",
+ path: FavoritesData.DESKTOP_PATH,
+ icon: await fileThumbnail("desktop", "folder", false),
+ },
+ {
+ name: "Documents",
+ path: FavoritesData.DOCUMENT_PATH,
+ icon: await fileThumbnail("document", "folder", false),
+ },
+ {
+ name: "Downloads",
+ path: FavoritesData.DOWNLOAD_PATH,
+ icon: await fileThumbnail("download", "folder", false),
+ },
+ {
+ name: "Pictures",
+ path: FavoritesData.PICTURE_PATH,
+ icon: await fileThumbnail("picture", "folder", false),
+ },
+ {
+ name: "Music",
+ path: FavoritesData.MUSIC_PATH,
+ icon: await fileThumbnail("music", "folder", false),
+ },
+ {
+ name: "Videos",
+ path: FavoritesData.VIDEO_PATH,
+ icon: await fileThumbnail("video", "folder", false),
+ },
+ {
+ name: "Trash",
+ path: "xplorer://Trash",
+ icon: await fileThumbnail("trash", "folder", false),
+ },
+ ];
}
diff --git a/src/Components/Favorites/favorites.ts b/src/Components/Favorites/favorites.ts
index 9a2c10eb..31b15916 100644
--- a/src/Components/Favorites/favorites.ts
+++ b/src/Components/Favorites/favorites.ts
@@ -1,12 +1,12 @@
-import Translate from '../I18n/i18n';
-import fileThumbnail from '../Thumbnail/thumbnail';
-import DirectoryAPI from '../../Service/directory';
-import Storage from '../../Service/storage';
-import defaultFavorites from './defaultFavorites';
-import FileAPI from '../../Service/files';
+import DirectoryAPI from "../../Service/directory";
+import FileAPI from "../../Service/files";
+import Storage from "../../Service/storage";
+import Translate from "../I18n/i18n";
+import fileThumbnail from "../Thumbnail/thumbnail";
+import defaultFavorites from "./defaultFavorites";
const isDefaultFavorite = async (filePath: string) => {
- return (await defaultFavorites()).some((favorite) => favorite.path === filePath);
+ return (await defaultFavorites()).some((favorite) => favorite.path === filePath);
};
/**
@@ -14,28 +14,28 @@ const isDefaultFavorite = async (filePath: string) => {
* @returns {{Promise} Favorites section HTML code
*/
const Favorites = async (): Promise => {
- const data = await Storage.get('favorites'); // Get user favorites data on sidebar
- const favorites = data?.favorites ?? (await defaultFavorites());
+ const data = await Storage.get("favorites"); // Get user favorites data on sidebar
+ const favorites = data?.favorites ?? (await defaultFavorites());
- let result = '';
- result += `${await Translate('Favorites')}
`;
- const defaultFavoritesList = (await defaultFavorites()).map((favorite) => favorite.name);
- for (const favorite of favorites) {
- if (!favorite.path) continue;
- if (favorite.path === 'xplorer://Home') continue;
- const fileData = new FileAPI(favorite.path);
- const exists = await fileData.exists();
- if (!exists && !(await isDefaultFavorite(favorite.path))) continue;
- let icon = favorite.icon;
- if (!icon) {
- if (defaultFavoritesList.indexOf(favorite.name) === -1) {
- const isdir = await new DirectoryAPI(favorite.path).isDir();
- icon = await fileThumbnail(exists ? favorite.path : favorite.name, isdir ? 'folder' : 'file', false);
- } else {
- icon = await fileThumbnail(favorite.name.toLowerCase(), 'folder', false);
- }
- }
- result += `${await Translate("Favorites")}`;
+ const defaultFavoritesList = (await defaultFavorites()).map((favorite) => favorite.name);
+ for (const favorite of favorites) {
+ if (!favorite.path) continue;
+ if (favorite.path === "xplorer://Home") continue;
+ const fileData = new FileAPI(favorite.path);
+ const exists = await fileData.exists();
+ if (!exists && !(await isDefaultFavorite(favorite.path))) continue;
+ let icon = favorite.icon;
+ if (!icon) {
+ if (defaultFavoritesList.indexOf(favorite.name) === -1) {
+ const isdir = await new DirectoryAPI(favorite.path).isDir();
+ icon = await fileThumbnail(exists ? favorite.path : favorite.name, isdir ? "folder" : "file", false);
+ } else {
+ icon = await fileThumbnail(favorite.name.toLowerCase(), "folder", false);
+ }
+ }
+ result += `
=> {
/>${await Translate(favorite.name)}
`;
- }
- return result;
+ }
+ return result;
};
export default Favorites;
diff --git a/src/Components/File/DetailFile.tsx b/src/Components/File/DetailFile.tsx
index b976e8c3..b8540691 100644
--- a/src/Components/File/DetailFile.tsx
+++ b/src/Components/File/DetailFile.tsx
@@ -1,8 +1,8 @@
-import React, { MouseEvent } from "react";
-import FileMetaData from "../../Typings/fileMetaData";
-import { ThemedButton, ThemedSpan } from "../Theme";
+import React, { type MouseEvent } from "react";
import { useSelector } from "react-redux";
-import { IAppState } from "../../Store/Reducers";
+import type { IAppState } from "../../Store/Reducers";
+import type FileMetaData from "../../Typings/fileMetaData";
+import { ThemedButton, ThemedSpan } from "../Theme";
export interface IDetailFileProps {
metadata: FileMetaData;
handleFileRightClick: (e: MouseEvent
, path: string) => void;
diff --git a/src/Components/File/GridFile.tsx b/src/Components/File/GridFile.tsx
index 62b523f8..8e424ae0 100644
--- a/src/Components/File/GridFile.tsx
+++ b/src/Components/File/GridFile.tsx
@@ -1,5 +1,5 @@
-import React, { MouseEvent } from "react";
-import FileMetaData from "../../Typings/fileMetaData";
+import React, { type MouseEvent } from "react";
+import type FileMetaData from "../../Typings/fileMetaData";
import { ThemedButton } from "../Theme";
export interface IGridFileProps {
diff --git a/src/Components/File/index.tsx b/src/Components/File/index.tsx
index 2daed9ef..53498afc 100644
--- a/src/Components/File/index.tsx
+++ b/src/Components/File/index.tsx
@@ -1,14 +1,11 @@
-import React, { MouseEvent } from "react";
+import React, { type MouseEvent } from "react";
import { useDispatch, useSelector } from "react-redux";
-import GridFile from "./GridFile";
import DetailFile from "./DetailFile";
+import GridFile from "./GridFile";
import { getStandardPath } from "../../Helpers/paths";
-import { openFileRequest, openFilePreview } from "../../Store/ActionCreators/FilesActionCreators";
-import { setActiveTab, updateTab } from "../../Store/ActionCreators/TabActionCreators";
-import { IAppState } from "../../Store/Reducers";
-import FileMetaData from "../../Typings/fileMetaData";
+import { openFilePreview, openFileRequest } from "../../Store/ActionCreators/FilesActionCreators";
import { updateSelection } from "../../Store/ActionCreators/SelectionActionCreators";
import { sortFiles } from "../MainView";
@@ -60,7 +57,13 @@ export const File = ({ mode, metadata }: IFileProps): JSX.Element => {
name: filePath.split("\\").pop() || "",
}),
);
- dispatch(setActiveTab({ name: filePath.split("\\").pop() || "", path: getStandardPath(filePath), id: activeTab.id }));
+ dispatch(
+ setActiveTab({
+ name: filePath.split("\\").pop() || "",
+ path: getStandardPath(filePath),
+ id: activeTab.id,
+ }),
+ );
};
switch (mode) {
diff --git a/src/Components/Files/File Operation/copy.ts b/src/Components/Files/File Operation/copy.ts
index 0418f049..aefa4799 100644
--- a/src/Components/Files/File Operation/copy.ts
+++ b/src/Components/Files/File Operation/copy.ts
@@ -1,4 +1,4 @@
-import Storage from '../../../Service/storage';
+import Storage from "../../../Service/storage";
/**
* Copy (a) file/s
*
@@ -6,7 +6,7 @@ import Storage from '../../../Service/storage';
* @returns {void}
*/
const Copy = (files: Array): void => {
- Storage.set('clipboard', { command: 'cp', files: files });
+ Storage.set("clipboard", { command: "cp", files: files });
};
export default Copy;
diff --git a/src/Components/Files/File Operation/cut.ts b/src/Components/Files/File Operation/cut.ts
index 6228221d..816dd245 100644
--- a/src/Components/Files/File Operation/cut.ts
+++ b/src/Components/Files/File Operation/cut.ts
@@ -1,4 +1,4 @@
-import Storage from '../../../Service/storage';
+import Storage from "../../../Service/storage";
/**
* Cut (a) file/s
*
@@ -6,25 +6,26 @@ import Storage from '../../../Service/storage';
* @returns {any}
*/
const Cut = (files: Array): void => {
- Storage.set('clipboard', { command: 'cut', files: files });
- for (const file of files) {
- document.querySelector(`.file[data-path="${encodeURI(file)}"]`).classList.add('cut');
- }
+ Storage.set("clipboard", { command: "cut", files: files });
+ for (const file of files) {
+ document.querySelector(`.file[data-path="${encodeURI(file)}"]`).classList.add("cut");
+ }
- (async function detectClipboardChange() {
- let n: NodeJS.Timeout; //eslint-disable-line
- if ((await Storage.get('clipboard')).files !== files) {
- global.clearTimeout(n);
- document.querySelectorAll('.file.cut').forEach((file) => {
- if (files.indexOf(decodeURI(file.dataset.path)) !== -1) {
- file.classList.remove('cut');
- }
- });
- return;
- }
- n = setTimeout(detectClipboardChange, 100);
- })();
- return;
+ (async function detectClipboardChange() {
+ // biome-ignore lint/style/useConst: Recursively assigned
+ let n: NodeJS.Timeout; //eslint-disable-line
+ if ((await Storage.get("clipboard")).files !== files) {
+ global.clearTimeout(n);
+ document.querySelectorAll(".file.cut").forEach((file) => {
+ if (files.indexOf(decodeURI(file.dataset.path)) !== -1) {
+ file.classList.remove("cut");
+ }
+ });
+ return;
+ }
+ n = setTimeout(detectClipboardChange, 100);
+ })();
+ return;
};
export default Cut;
diff --git a/src/Components/Files/File Operation/location.ts b/src/Components/Files/File Operation/location.ts
index bf5d0ca3..2d5fcd4d 100644
--- a/src/Components/Files/File Operation/location.ts
+++ b/src/Components/Files/File Operation/location.ts
@@ -1,4 +1,4 @@
-import { writeTextToClipboard } from '../../../Service/clipboard';
+import { writeTextToClipboard } from "../../../Service/clipboard";
/**
* Copy file location from file element into clipborad
@@ -6,8 +6,8 @@ import { writeTextToClipboard } from '../../../Service/clipboard';
* @returns {void}
*/
const copyLocation = (element: HTMLElement): void => {
- const path = decodeURI(element.dataset.path);
- writeTextToClipboard(path);
+ const path = decodeURI(element.dataset.path);
+ writeTextToClipboard(path);
};
export default copyLocation;
diff --git a/src/Components/Files/File Operation/new.ts b/src/Components/Files/File Operation/new.ts
index 1e44bcdb..8489e202 100644
--- a/src/Components/Files/File Operation/new.ts
+++ b/src/Components/Files/File Operation/new.ts
@@ -1,7 +1,7 @@
-import { OperationLog } from '../../Functions/log';
-import focusingPath from '../../Functions/focusingPath';
-import FileAPI from '../../../Service/files';
-import PromptError from '../../Prompt/error';
+import FileAPI from "../../../Service/files";
+import focusingPath from "../../Functions/focusingPath";
+import { OperationLog } from "../../Functions/log";
+import PromptError from "../../Prompt/error";
/**
* Create a new file
@@ -11,24 +11,24 @@ import PromptError from '../../Prompt/error';
* @returns {Promise}
*/
const NewFile = async (fileName: string, parentDir?: string, writeLog = true): Promise => {
- if (!parentDir) parentDir = await focusingPath();
- const newFile = new FileAPI(fileName, parentDir);
+ if (!parentDir) parentDir = await focusingPath();
+ const newFile = new FileAPI(fileName, parentDir);
- if (await newFile.exists()) {
- PromptError('Error creating file', `Failed to create file ${newFile.fileName}: File already existed`);
- } else {
- try {
- await newFile.createFile();
- } catch (err) {
- PromptError('Error creating file', `Failed to create file ${newFile.fileName}: Something went wrong (${err})`);
- }
+ if (await newFile.exists()) {
+ PromptError("Error creating file", `Failed to create file ${newFile.fileName}: File already existed`);
+ } else {
+ try {
+ await newFile.createFile();
+ } catch (err) {
+ PromptError("Error creating file", `Failed to create file ${newFile.fileName}: Something went wrong (${err})`);
+ }
- if (writeLog) {
- if (typeof newFile.fileName === 'string') {
- OperationLog('newfile', null, newFile.fileName);
- }
- }
- }
+ if (writeLog) {
+ if (typeof newFile.fileName === "string") {
+ OperationLog("newfile", null, newFile.fileName);
+ }
+ }
+ }
};
export default NewFile;
diff --git a/src/Components/Files/File Operation/paste.ts b/src/Components/Files/File Operation/paste.ts
index ee62fdf4..e84ceb86 100644
--- a/src/Components/Files/File Operation/paste.ts
+++ b/src/Components/Files/File Operation/paste.ts
@@ -1,81 +1,80 @@
-import Storage from '../../../Service/storage';
-import OperationAPI from '../../../Service/operation';
-import DirectoryAPI from '../../../Service/directory';
-import joinPath from '../../Functions/path/joinPath';
-import getBasename from '../../Functions/path/basename';
-import getDirname from '../../Functions/path/dirname';
-import NormalizeSlash from '../../Functions/path/normalizeSlash';
-import FileAPI from '../../../Service/files';
-import ConfirmDialog from '../../Prompt/confirm';
-import normalizeSlash from '../../Functions/path/normalizeSlash';
-import { OperationLog } from '../../Functions/log';
+import DirectoryAPI from "../../../Service/directory";
+import FileAPI from "../../../Service/files";
+import OperationAPI from "../../../Service/operation";
+import Storage from "../../../Service/storage";
+import { OperationLog } from "../../Functions/log";
+import getBasename from "../../Functions/path/basename";
+import getDirname from "../../Functions/path/dirname";
+import joinPath from "../../Functions/path/joinPath";
+import NormalizeSlash from "../../Functions/path/normalizeSlash";
+import normalizeSlash from "../../Functions/path/normalizeSlash";
+import ConfirmDialog from "../../Prompt/confirm";
const cpy = async (src: string, dest: string) => {
- dest = joinPath(dest, getBasename(src));
- if (NormalizeSlash(getDirname(src)) === NormalizeSlash(dest)) {
- dest += ' - COPY';
- }
- if (await new FileAPI(dest).exists()) {
- if (!(await ConfirmDialog('Target file exists', 'Target directory with the same file name exists, do you want to overwrite it?', 'No')))
- return;
- }
- new OperationAPI(src, dest).copyFile();
+ dest = joinPath(dest, getBasename(src));
+ if (NormalizeSlash(getDirname(src)) === NormalizeSlash(dest)) {
+ dest += " - COPY";
+ }
+ if (await new FileAPI(dest).exists()) {
+ if (!(await ConfirmDialog("Target file exists", "Target directory with the same file name exists, do you want to overwrite it?", "No")))
+ return;
+ }
+ new OperationAPI(src, dest).copyFile();
};
const Paste = async (target: string): Promise => {
- const clipboard = await Storage.get('clipboard');
+ const clipboard = await Storage.get("clipboard");
- const recuriveCopy = async (_path: string, _target: string, firstRecursion = false) => {
- let useCopySuffix = false;
- if (firstRecursion && normalizeSlash(getDirname(_path)) === normalizeSlash(_target)) {
- useCopySuffix = true;
- }
- const subdirInfo = new DirectoryAPI(normalizeSlash(_path));
- if (await subdirInfo.isDir()) {
- let _dest = joinPath(_target, getBasename(_path));
- if (useCopySuffix) _dest += '- COPY';
- await new DirectoryAPI(_dest).mkdir();
- (await subdirInfo.getFiles()).files.forEach(async (subsubdir) => {
- await recuriveCopy(subsubdir.file_path, _dest);
- });
- } else {
- cpy(_path, _target);
- }
- };
- for (const file of clipboard.files) {
- const dirInfo = new DirectoryAPI(file);
- const dest = joinPath(target, getBasename(file));
- switch (clipboard.command) {
- case 'cp':
- if (await dirInfo.isDir()) {
- await recuriveCopy(file, target, true);
- } else {
- cpy(file, target);
- }
- break;
- case 'cut':
- if (await new DirectoryAPI(dest).exists()) {
- if (
- !(await ConfirmDialog(
- 'Target file exists',
- 'Target directory with the same file/dir name exists, do you want to overwrite it?',
- 'No'
- ))
- )
- return;
- else {
- await new OperationAPI(dest).unlink();
- }
- }
- await new OperationAPI(file, dest).cut();
- break;
- }
- }
- switch (clipboard.command) {
- case 'cp':
- OperationLog('copy', clipboard.files, target);
- break;
- case 'cut':
- OperationLog('cut', clipboard.files, target);
- break;
- }
+ const recuriveCopy = async (_path: string, _target: string, firstRecursion = false) => {
+ let useCopySuffix = false;
+ if (firstRecursion && normalizeSlash(getDirname(_path)) === normalizeSlash(_target)) {
+ useCopySuffix = true;
+ }
+ const subdirInfo = new DirectoryAPI(normalizeSlash(_path));
+ if (await subdirInfo.isDir()) {
+ let _dest = joinPath(_target, getBasename(_path));
+ if (useCopySuffix) _dest += "- COPY";
+ await new DirectoryAPI(_dest).mkdir();
+ (await subdirInfo.getFiles()).files.forEach(async (subsubdir) => {
+ await recuriveCopy(subsubdir.file_path, _dest);
+ });
+ } else {
+ cpy(_path, _target);
+ }
+ };
+ for (const file of clipboard.files) {
+ const dirInfo = new DirectoryAPI(file);
+ const dest = joinPath(target, getBasename(file));
+ switch (clipboard.command) {
+ case "cp":
+ if (await dirInfo.isDir()) {
+ await recuriveCopy(file, target, true);
+ } else {
+ cpy(file, target);
+ }
+ break;
+ case "cut":
+ if (await new DirectoryAPI(dest).exists()) {
+ if (
+ !(await ConfirmDialog(
+ "Target file exists",
+ "Target directory with the same file/dir name exists, do you want to overwrite it?",
+ "No",
+ ))
+ )
+ return;
+
+ await new OperationAPI(dest).unlink();
+ }
+ await new OperationAPI(file, dest).cut();
+ break;
+ }
+ }
+ switch (clipboard.command) {
+ case "cp":
+ OperationLog("copy", clipboard.files, target);
+ break;
+ case "cut":
+ OperationLog("cut", clipboard.files, target);
+ break;
+ }
};
export default Paste;
diff --git a/src/Components/Files/File Operation/pin.ts b/src/Components/Files/File Operation/pin.ts
index fc02eec9..d385d014 100644
--- a/src/Components/Files/File Operation/pin.ts
+++ b/src/Components/Files/File Operation/pin.ts
@@ -1,11 +1,11 @@
-import createSidebar from '../../Layout/sidebar';
-import defaultFavorites from '../../Favorites/defaultFavorites';
-import Storage from '../../../Service/storage';
-import basename from '../../Functions/path/basename';
+import Storage from "../../../Service/storage";
+import defaultFavorites from "../../Favorites/defaultFavorites";
+import basename from "../../Functions/path/basename";
+import createSidebar from "../../Layout/sidebar";
interface Favorites {
- name: string;
- path: string;
- icon: string;
+ name: string;
+ path: string;
+ icon: string;
}
/**
@@ -14,18 +14,18 @@ interface Favorites {
* @returns {void}
*/
const Pin = async (filePaths: string[]): Promise => {
- const data = await Storage.get('favorites');
- let favorites = data?.favorites ?? [{ path: 'xplorer://Home', name: 'Home' }, ...(await defaultFavorites())];
- favorites.forEach((v: Favorites) => delete v.icon);
- for (const filePath of filePaths) {
- if (favorites.filter((favorite: Favorites) => favorite.path === filePath).length) {
- favorites = favorites.filter((favorite: Favorites) => favorite.path !== filePath);
- } else {
- favorites.push({ name: basename(filePath), path: filePath });
- }
- }
- Storage.set('favorites', { favorites });
- createSidebar();
+ const data = await Storage.get("favorites");
+ let favorites = data?.favorites ?? [{ path: "xplorer://Home", name: "Home" }, ...(await defaultFavorites())];
+ favorites.forEach((v: Favorites) => delete v.icon);
+ for (const filePath of filePaths) {
+ if (favorites.filter((favorite: Favorites) => favorite.path === filePath).length) {
+ favorites = favorites.filter((favorite: Favorites) => favorite.path !== filePath);
+ } else {
+ favorites.push({ name: basename(filePath), path: filePath });
+ }
+ }
+ Storage.set("favorites", { favorites });
+ createSidebar();
};
export default Pin;
diff --git a/src/Components/Files/File Operation/redo.ts b/src/Components/Files/File Operation/redo.ts
index b9f3070c..4fff770d 100644
--- a/src/Components/Files/File Operation/redo.ts
+++ b/src/Components/Files/File Operation/redo.ts
@@ -1,68 +1,67 @@
-import Copy from './copy';
-import Paste from './paste';
-import windowName from '../../../Service/window';
-import Storage from '../../../Service/storage';
-import NewFile from './new';
-import getBasename from '../../Functions/path/basename';
-import getDirname from '../../Functions/path/dirname';
-import NewFolder from '../../Folder/new';
-import DirectoryAPI from '../../../Service/directory';
-import ConfirmDialog from '../../Prompt/confirm';
-import OperationAPI from '../../../Service/operation';
-import joinPath from '../../Functions/path/joinPath';
-import { Trash } from './trash';
+import DirectoryAPI from "../../../Service/directory";
+import OperationAPI from "../../../Service/operation";
+import Storage from "../../../Service/storage";
+import windowName from "../../../Service/window";
+import NewFolder from "../../Folder/new";
+import getBasename from "../../Functions/path/basename";
+import getDirname from "../../Functions/path/dirname";
+import joinPath from "../../Functions/path/joinPath";
+import ConfirmDialog from "../../Prompt/confirm";
+import Copy from "./copy";
+import NewFile from "./new";
+import Paste from "./paste";
+import { Trash } from "./trash";
/**
* Redo the _undo_ed Operation
* @returns {Promise}
*/
const Redo = async (): Promise => {
- const operationLogs = await Storage.get(`operations-${windowName}`);
- const latestOperation = operationLogs.operations[operationLogs.currentIndex + 1];
- const increaseIndex = () => {
- if (operationLogs.currentIndex + 2 >= operationLogs.operations.length) operationLogs.currentIndex = operationLogs.currentIndex + 1;
- };
- switch (latestOperation.operationType) {
- case 'copy':
- Copy(latestOperation.sources);
- Paste(latestOperation.destination);
- break;
- case 'cut':
- for (const source of latestOperation.sources) {
- const dest = joinPath(latestOperation.destination, getBasename(source));
- if (await new DirectoryAPI(dest).exists()) {
- if (
- !(await ConfirmDialog(
- 'Target file exists',
- 'Target directory with the same file/dir name exists, do you want to overwrite it?',
- 'No'
- ))
- )
- return;
- else {
- await new OperationAPI(dest).unlink();
- }
- }
- await new OperationAPI(source, dest).rename();
- }
- increaseIndex();
- break;
- case 'newfile':
- NewFile(getBasename(latestOperation.destination), getDirname(latestOperation.destination), false);
- increaseIndex();
- break;
- case 'newfolder':
- NewFolder(getBasename(latestOperation.destination), getDirname(latestOperation.destination), false);
- increaseIndex();
- break;
- case 'delete':
- Trash(latestOperation.sources);
- increaseIndex();
- break;
- case 'rename':
- await new OperationAPI(latestOperation.sources, latestOperation.destination).rename();
- increaseIndex();
- break;
- }
- Storage.set(`operations-${windowName}`, operationLogs);
+ const operationLogs = await Storage.get(`operations-${windowName}`);
+ const latestOperation = operationLogs.operations[operationLogs.currentIndex + 1];
+ const increaseIndex = () => {
+ if (operationLogs.currentIndex + 2 >= operationLogs.operations.length) operationLogs.currentIndex = operationLogs.currentIndex + 1;
+ };
+ switch (latestOperation.operationType) {
+ case "copy":
+ Copy(latestOperation.sources);
+ Paste(latestOperation.destination);
+ break;
+ case "cut":
+ for (const source of latestOperation.sources) {
+ const dest = joinPath(latestOperation.destination, getBasename(source));
+ if (await new DirectoryAPI(dest).exists()) {
+ if (
+ !(await ConfirmDialog(
+ "Target file exists",
+ "Target directory with the same file/dir name exists, do you want to overwrite it?",
+ "No",
+ ))
+ )
+ return;
+
+ await new OperationAPI(dest).unlink();
+ }
+ await new OperationAPI(source, dest).rename();
+ }
+ increaseIndex();
+ break;
+ case "newfile":
+ NewFile(getBasename(latestOperation.destination), getDirname(latestOperation.destination), false);
+ increaseIndex();
+ break;
+ case "newfolder":
+ NewFolder(getBasename(latestOperation.destination), getDirname(latestOperation.destination), false);
+ increaseIndex();
+ break;
+ case "delete":
+ Trash(latestOperation.sources);
+ increaseIndex();
+ break;
+ case "rename":
+ await new OperationAPI(latestOperation.sources, latestOperation.destination).rename();
+ increaseIndex();
+ break;
+ }
+ Storage.set(`operations-${windowName}`, operationLogs);
};
export default Redo;
diff --git a/src/Components/Files/File Operation/rename.ts b/src/Components/Files/File Operation/rename.ts
index 09a7f8dd..9c99341c 100644
--- a/src/Components/Files/File Operation/rename.ts
+++ b/src/Components/Files/File Operation/rename.ts
@@ -1,32 +1,32 @@
-import { OperationLog } from '../../Functions/log';
-import focusingPath from '../../Functions/focusingPath';
-import Ask from '../../Prompt/ask';
-import basename from '../../Functions/path/basename';
-import getDirname from '../../Functions/path/dirname';
-import joinPath from '../../Functions/path/joinPath';
-import OperationAPI from '../../../Service/operation';
-import PromptError from '../../Prompt/error';
-import FileAPI from '../../../Service/files';
-import ConfirmDialog from '../../Prompt/confirm';
+import FileAPI from "../../../Service/files";
+import OperationAPI from "../../../Service/operation";
+import focusingPath from "../../Functions/focusingPath";
+import { OperationLog } from "../../Functions/log";
+import basename from "../../Functions/path/basename";
+import getDirname from "../../Functions/path/dirname";
+import joinPath from "../../Functions/path/joinPath";
+import Ask from "../../Prompt/ask";
+import ConfirmDialog from "../../Prompt/confirm";
+import PromptError from "../../Prompt/error";
/**
* Rename file/folder name
* @param {string} path - location of the file/folder
* @returns {void}
*/
const Rename = (filePath: string): void => {
- Ask('Rename', 'New File Name:', { value: basename(filePath) }).then(async (newName: string) => {
- const target = getDirname(newName) === '.' ? joinPath(await focusingPath(), newName) : joinPath(getDirname(filePath), newName);
- if (await new FileAPI(target).exists()) {
- const confirm = await ConfirmDialog('File Exists', 'The new name already exists, do you want to overwrite it?', 'No');
- if (!confirm) return;
- }
- try {
- new OperationAPI(filePath, target).rename();
- } catch (err) {
- PromptError('Error renaming file', `Failed to rename ${filePath} [${err}]`);
- }
- OperationLog('rename', decodeURI(filePath), target);
- });
+ Ask("Rename", "New File Name:", { value: basename(filePath) }).then(async (newName: string) => {
+ const target = getDirname(newName) === "." ? joinPath(await focusingPath(), newName) : joinPath(getDirname(filePath), newName);
+ if (await new FileAPI(target).exists()) {
+ const confirm = await ConfirmDialog("File Exists", "The new name already exists, do you want to overwrite it?", "No");
+ if (!confirm) return;
+ }
+ try {
+ new OperationAPI(filePath, target).rename();
+ } catch (err) {
+ PromptError("Error renaming file", `Failed to rename ${filePath} [${err}]`);
+ }
+ OperationLog("rename", decodeURI(filePath), target);
+ });
};
export default Rename;
diff --git a/src/Components/Files/File Operation/search.ts b/src/Components/Files/File Operation/search.ts
index 2c88a9e6..ea15e48d 100644
--- a/src/Components/Files/File Operation/search.ts
+++ b/src/Components/Files/File Operation/search.ts
@@ -1,20 +1,20 @@
-import Translate from '../../I18n/i18n';
-import focusingPath from '../../Functions/focusingPath';
-import DirectoryAPI from '../../../Service/directory';
-import { startLoading, stopLoading } from '../../Functions/Loading/loading';
-import displayFiles from '../../Open/displayFiles';
-import changePosition from '../../Functions/changePosition';
-import { LOAD_IMAGE } from '../../Functions/lazyLoadingImage';
-import { updateTheme } from '../../Theme/theme';
-import { OpenLog } from '../../Functions/log';
-import { OpenDir } from '../../Open/open';
-import isTauri from '../../../Util/is-tauri';
-import { GET_TAB_ELEMENT } from '../../../Util/constants';
+import DirectoryAPI from "../../../Service/directory";
+import { GET_TAB_ELEMENT } from "../../../Util/constants";
+import isTauri from "../../../Util/is-tauri";
+import { startLoading, stopLoading } from "../../Functions/Loading/loading";
+import changePosition from "../../Functions/changePosition";
+import focusingPath from "../../Functions/focusingPath";
+import { LOAD_IMAGE } from "../../Functions/lazyLoadingImage";
+import { OpenLog } from "../../Functions/log";
+import Translate from "../../I18n/i18n";
+import displayFiles from "../../Open/displayFiles";
+import { OpenDir } from "../../Open/open";
+import { updateTheme } from "../../Theme/theme";
let being_watch: string;
const stopSearchingProcess = async (): Promise => {
- being_watch = null;
- if (await new DirectoryAPI('').stopSearching()) stopLoading();
+ being_watch = null;
+ if (await new DirectoryAPI("").stopSearching()) stopLoading();
};
/**
@@ -24,33 +24,33 @@ const stopSearchingProcess = async (): Promise => {
* @returns {Promise}
*/
const processSearch = async (to_search: string, search_in: string): Promise => {
- const MAIN_ELEMENT = GET_TAB_ELEMENT();
- MAIN_ELEMENT.innerHTML = '';
- if (!to_search.length) OpenDir(search_in);
- startLoading();
- const search_path = `Search: [[${to_search}]] inside [[${search_in}]]`;
- changePosition(search_path);
- let foundSomething = false;
+ const MAIN_ELEMENT = GET_TAB_ELEMENT();
+ MAIN_ELEMENT.innerHTML = "";
+ if (!to_search.length) OpenDir(search_in);
+ startLoading();
+ const search_path = `Search: [[${to_search}]] inside [[${search_in}]]`;
+ changePosition(search_path);
+ let foundSomething = false;
- const finalResult = await new DirectoryAPI(search_in).search(to_search, async (partialFound) => {
- let _el = document.createElement('div') as HTMLElement;
- foundSomething = true;
- if (document.querySelector('.path-navigator').value.startsWith('Search: '))
- _el = await displayFiles(partialFound, search_path, _el, null, true);
- for (const childEl of _el.children) {
- MAIN_ELEMENT.appendChild(childEl);
- }
- });
- const _el = document.createElement('div');
- MAIN_ELEMENT.appendChild(await displayFiles(finalResult, search_path, _el, null, true));
- if (!finalResult.length && !foundSomething) {
- MAIN_ELEMENT.classList.add('empty-dir-notification');
- MAIN_ELEMENT.innerText = "Can't find specified query";
- }
- stopLoading();
- updateTheme('grid');
- LOAD_IMAGE();
- OpenLog(search_path);
+ const finalResult = await new DirectoryAPI(search_in).search(to_search, async (partialFound) => {
+ let _el = document.createElement("div") as HTMLElement;
+ foundSomething = true;
+ if (document.querySelector(".path-navigator").value.startsWith("Search: "))
+ _el = await displayFiles(partialFound, search_path, _el, null, true);
+ for (const childEl of _el.children) {
+ MAIN_ELEMENT.appendChild(childEl);
+ }
+ });
+ const _el = document.createElement("div");
+ MAIN_ELEMENT.appendChild(await displayFiles(finalResult, search_path, _el, null, true));
+ if (!finalResult.length && !foundSomething) {
+ MAIN_ELEMENT.classList.add("empty-dir-notification");
+ MAIN_ELEMENT.innerText = "Can't find specified query";
+ }
+ stopLoading();
+ updateTheme("grid");
+ LOAD_IMAGE();
+ OpenLog(search_path);
};
/**
@@ -58,57 +58,58 @@ const processSearch = async (to_search: string, search_in: string): Promise}
*/
const getFocusingPath = async (): Promise => {
- let _focusingPath = await focusingPath();
- if (_focusingPath.startsWith('Search: ')) {
- const splitByInsideKeyword = _focusingPath.split(' inside ');
- if (splitByInsideKeyword.length === 2) {
- _focusingPath = splitByInsideKeyword[1].slice(2, -2);
- } else {
- for (let i = 0; i < splitByInsideKeyword.length; i++) {
- if (splitByInsideKeyword[i]?.endsWith(']]') && splitByInsideKeyword[i + 1]?.startsWith('[[')) {
- _focusingPath = splitByInsideKeyword
- .slice(i + 1)
- .join(' inside ')
- .slice(2, -2);
- }
- }
- }
- }
- return _focusingPath;
+ let _focusingPath = await focusingPath();
+ if (_focusingPath.startsWith("Search: ")) {
+ const splitByInsideKeyword = _focusingPath.split(" inside ");
+ if (splitByInsideKeyword.length === 2) {
+ _focusingPath = splitByInsideKeyword[1].slice(2, -2);
+ } else {
+ for (let i = 0; i < splitByInsideKeyword.length; i++) {
+ if (splitByInsideKeyword[i]?.endsWith("]]") && splitByInsideKeyword[i + 1]?.startsWith("[[")) {
+ _focusingPath = splitByInsideKeyword
+ .slice(i + 1)
+ .join(" inside ")
+ .slice(2, -2);
+ }
+ }
+ }
+ }
+ return _focusingPath;
};
/**
* Initialize search feature in Xplorer
* @returns {Promise}
*/
const Search = async (): Promise => {
- let listener: ReturnType;
- const searchElement = document.querySelector('.search-bar');
- if (!isTauri) {
- searchElement.setAttribute('disabled', '');
- }
- searchElement.placeholder = `🔎 ${await Translate('Search')}`;
- searchElement.addEventListener('keydown', async (e: KeyboardEvent) => {
- clearTimeout(listener);
- if (e.ctrlKey && e.key === 'f') {
- return;
- } else if (e.key === 'Enter') {
- const value = (e.target as HTMLInputElement).value;
- if (value !== being_watch) {
- processSearch(value, await getFocusingPath());
- being_watch = value;
- }
- } else {
- listener = setTimeout(async () => {
- const value = (e.target as HTMLInputElement).value;
- if (value === '') {
- OpenDir(await getFocusingPath());
- } else if (value !== being_watch) {
- processSearch(value, await getFocusingPath());
- being_watch = value;
- }
- }, 1000);
- }
- });
+ let listener: ReturnType;
+ const searchElement = document.querySelector(".search-bar");
+ if (!isTauri) {
+ searchElement.setAttribute("disabled", "");
+ }
+ searchElement.placeholder = `🔎 ${await Translate("Search")}`;
+ searchElement.addEventListener("keydown", async (e: KeyboardEvent) => {
+ clearTimeout(listener);
+ if (e.ctrlKey && e.key === "f") {
+ return;
+ }
+ if (e.key === "Enter") {
+ const value = (e.target as HTMLInputElement).value;
+ if (value !== being_watch) {
+ processSearch(value, await getFocusingPath());
+ being_watch = value;
+ }
+ } else {
+ listener = setTimeout(async () => {
+ const value = (e.target as HTMLInputElement).value;
+ if (value === "") {
+ OpenDir(await getFocusingPath());
+ } else if (value !== being_watch) {
+ processSearch(value, await getFocusingPath());
+ being_watch = value;
+ }
+ }, 1000);
+ }
+ });
};
export default Search;
export { processSearch, stopSearchingProcess };
diff --git a/src/Components/Files/File Operation/select.ts b/src/Components/Files/File Operation/select.ts
index 57a1abc9..3771ebff 100644
--- a/src/Components/Files/File Operation/select.ts
+++ b/src/Components/Files/File Operation/select.ts
@@ -1,19 +1,19 @@
-import { elementClassNameContains } from '../../Functions/elementClassNameContains';
-import Storage from '../../../Service/storage';
-import { UpdateInfo } from '../../Layout/infobar';
-import FileAPI from '../../../Service/files';
-import formatBytes from '../../Functions/filesize';
-import Preview from '../File Preview/preview';
-import { ensureElementInViewPort } from '../../Functions/viewport';
-import { MAIN_BOX_ELEMENT } from '../../../Util/constants';
-import { direction } from '../../../Typings/select';
+import FileAPI from "../../../Service/files";
+import Storage from "../../../Service/storage";
+import type { direction } from "../../../Typings/select";
+import { MAIN_BOX_ELEMENT } from "../../../Util/constants";
+import { elementClassNameContains } from "../../Functions/elementClassNameContains";
+import formatBytes from "../../Functions/filesize";
+import { ensureElementInViewPort } from "../../Functions/viewport";
+import { UpdateInfo } from "../../Layout/infobar";
+import Preview from "../File Preview/preview";
let latestSelected: HTMLElement;
let latestShiftSelected: HTMLElement;
//Mouse drag selection vars
class Point {
- private _x: number;
+ private _x: number;
private _y: number;
constructor(x = 0, y = 0) {
@@ -21,48 +21,63 @@ class Point {
this._y = y;
}
- public get x(): number { return this._x; }
- public get y(): number { return this._y; }
+ public get x(): number {
+ return this._x;
+ }
+ public get y(): number {
+ return this._y;
+ }
- public set x(newX: number) { this._x = newX; }
- public set y(newY: number) { this._y = newY; }
+ public set x(newX: number) {
+ this._x = newX;
+ }
+ public set y(newY: number) {
+ this._y = newY;
+ }
public Set(x: number, y: number) {
this.x = x;
this.y = y;
}
- public delta(target: Point) {
- return new Point(this.x - target.x, this.y - target.y);
- }
+ public delta(target: Point) {
+ return new Point(this.x - target.x, this.y - target.y);
+ }
}
-let selectingDiv: HTMLElement = document.createElement('div');
-selectingDiv.setAttribute('style', 'position: absolute; background-color: rgba(100, 100, 255, 0.2); z-index: 100;');
-let isSelecting: boolean = false;
+const selectingDiv: HTMLElement = document.createElement("div");
+selectingDiv.setAttribute("style", "position: absolute; background-color: rgba(100, 100, 255, 0.2); z-index: 100;");
+let isSelecting = false;
let selectingOrigin: Point;
let mainBoxBounds: DOMRect;
let selected = 0;
-const selectingDivBounds = { left: 0, top: 0, right: 0, bottom: 0, height: 0, width: 0 };
+const selectingDivBounds = {
+ left: 0,
+ top: 0,
+ right: 0,
+ bottom: 0,
+ height: 0,
+ width: 0,
+};
/**
* Call this function whenever user selected (a) file grid(s).
* @returns {Promise}
*/
const ChangeSelectedEvent = async (): Promise => {
- const selectedFileGrid = document.querySelectorAll('.file-grid.selected');
- if (!selectedFileGrid.length) UpdateInfo('selected-files', '');
- else {
- const selectedFilePaths = Array.from(selectedFileGrid).map((element) => decodeURI((element as HTMLElement).dataset.path));
- const total_sizes = await new FileAPI(selectedFilePaths).calculateFilesSize();
- UpdateInfo('selected-files', `${selectedFileGrid.length} file${selectedFileGrid.length > 1 ? 's' : ''} selected ${formatBytes(total_sizes)}`);
- if (
- selectedFilePaths.length === 1 &&
- document.querySelectorAll('.preview').length > 0 &&
- ((await Storage.get('preference'))?.automaticallyChangePreviewFile ?? true)
- ) {
- Preview(selectedFilePaths[0]);
- }
- }
+ const selectedFileGrid = document.querySelectorAll(".file-grid.selected");
+ if (!selectedFileGrid.length) UpdateInfo("selected-files", "");
+ else {
+ const selectedFilePaths = Array.from(selectedFileGrid).map((element) => decodeURI((element as HTMLElement).dataset.path));
+ const total_sizes = await new FileAPI(selectedFilePaths).calculateFilesSize();
+ UpdateInfo("selected-files", `${selectedFileGrid.length} file${selectedFileGrid.length > 1 ? "s" : ""} selected ${formatBytes(total_sizes)}`);
+ if (
+ selectedFilePaths.length === 1 &&
+ document.querySelectorAll(".preview").length > 0 &&
+ ((await Storage.get("preference"))?.automaticallyChangePreviewFile ?? true)
+ ) {
+ Preview(selectedFilePaths[0]);
+ }
+ }
};
/**
* Select a file grid...
@@ -78,32 +93,32 @@ const ChangeSelectedEvent = async (): Promise => {
* @returns {void}
*/
const Select = (element: HTMLElement, ctrl: boolean, shift: boolean): void => {
- if (!ctrl && !shift) unselectAllSelected();
- // add 'selected' class if element classlist does not contain it...
- if (!element.classList.contains('selected')) element.classList.add('selected');
- // ...Otherwise, remove it
- else element.classList.remove('selected');
- if (shift && latestSelected) {
- let start = false;
- for (const _element of document.querySelectorAll('.file')) {
- if (start) _element.classList.add('selected');
- else _element.classList.remove('selected');
- if (_element === latestSelected) {
- start = !start;
- _element.classList.add('selected');
- } else if (_element === element) {
- start = !start;
- _element.classList.add('selected');
- }
- }
- } else {
- const { getSelectedAllStatus } = require('../../Shortcut/shortcut'); //eslint-disable-line
- if (getSelectedAllStatus() && ctrl) return;
- latestSelected = element;
- latestShiftSelected = element;
- }
- ChangeSelectedEvent();
- ensureElementInViewPort(element);
+ if (!ctrl && !shift) unselectAllSelected();
+ // add 'selected' class if element classlist does not contain it...
+ if (!element.classList.contains("selected")) element.classList.add("selected");
+ // ...Otherwise, remove it
+ else element.classList.remove("selected");
+ if (shift && latestSelected) {
+ let start = false;
+ for (const _element of document.querySelectorAll(".file")) {
+ if (start) _element.classList.add("selected");
+ else _element.classList.remove("selected");
+ if (_element === latestSelected) {
+ start = !start;
+ _element.classList.add("selected");
+ } else if (_element === element) {
+ start = !start;
+ _element.classList.add("selected");
+ }
+ }
+ } else {
+ const { getSelectedAllStatus } = require("../../Shortcut/shortcut"); //eslint-disable-line
+ if (getSelectedAllStatus() && ctrl) return;
+ latestSelected = element;
+ latestShiftSelected = element;
+ }
+ ChangeSelectedEvent();
+ ensureElementInViewPort(element);
};
/**
@@ -111,15 +126,15 @@ const Select = (element: HTMLElement, ctrl: boolean, shift: boolean): void => {
* @returns {Promise}
*/
const selectFirstFile = async (): Promise => {
- const hideHiddenFiles = (await Storage.get('preference'))?.hideHiddenFiles ?? true;
- const firstFileElement = MAIN_BOX_ELEMENT().querySelector(`.file${hideHiddenFiles ? ':not([data-hidden-file])' : ''}`);
- firstFileElement.classList.add('selected');
- ChangeSelectedEvent();
- latestSelected = firstFileElement as HTMLElement;
+ const hideHiddenFiles = (await Storage.get("preference"))?.hideHiddenFiles ?? true;
+ const firstFileElement = MAIN_BOX_ELEMENT().querySelector(`.file${hideHiddenFiles ? ":not([data-hidden-file])" : ""}`);
+ firstFileElement.classList.add("selected");
+ ChangeSelectedEvent();
+ latestSelected = firstFileElement as HTMLElement;
};
const elementIndex = (element: HTMLElement): number => {
- return Array.from(element.parentNode.children).indexOf(element);
+ return Array.from(element.parentNode.children).indexOf(element);
};
/**
@@ -127,191 +142,200 @@ const elementIndex = (element: HTMLElement): number => {
* @returns {void}
*/
const SelectInit = (): void => {
- //Saving the main box element in a variable, better than calling MAIN_BOX_ELEMENT() everytime
- const MAIN_BOX_EL: HTMLElement = MAIN_BOX_ELEMENT();
- mainBoxBounds = MAIN_BOX_EL.getBoundingClientRect();
-
- MAIN_BOX_EL.addEventListener('click', (e) => {
- if (
- !(e.target as HTMLElement).className.split(' ').some(function (c) {
- return /file/.test(c);
- }) &&
- //If user doesn't drag the mouse
- (e.pageX - mainBoxBounds.left == selectingOrigin.x &&
- e.pageY - mainBoxBounds.top == selectingOrigin.y)
- ) {
- unselectAllSelected();
- latestSelected = null;
- latestShiftSelected = null;
- }
- let fileTarget = e.target as HTMLElement;
- while (fileTarget?.classList && !fileTarget.classList.contains('file')) fileTarget = fileTarget.parentNode as HTMLElement;
- if (fileTarget.id === 'workspace' || !fileTarget?.classList?.contains('file')) return;
-
- Select(fileTarget, e.ctrlKey, e.shiftKey);
- });
-
- MAIN_BOX_EL.addEventListener('mousedown', (e) => {
- /// SelectInit is called async, therefore bounds come before full element calculation.
- /// Here is to avoid wrong div placement, but we can find a better way without calling getBoundingClientRect each mousedown
- mainBoxBounds = MAIN_BOX_EL.getBoundingClientRect();
- //Start selection
- isSelecting = true;
- //Save selection starting point
- selectingOrigin = new Point(e.pageX - mainBoxBounds.left, e.pageY - mainBoxBounds.top);
- if (
- !(e.target as HTMLElement).className.split(' ').some(function (c) {
- return /file/.test(c);
- })
- ) {
- MAIN_BOX_EL.prepend(selectingDiv);
- selectingDiv.style.inset = selectingOrigin.y + 'px auto ' + selectingOrigin.x + 'px auto';
- selectingDiv.style.width = selectingDiv.style.height = 'auto';
- }
- });
-
- //Check when mouse is released. On window to catch events outside of main box
- window.addEventListener('mouseup', (e) => {
- if(isSelecting) {
- isSelecting = false;
-
- selectingDiv.style.inset = selectingDiv.style.width = selectingDiv.style.height = 'auto';
-
- selectingDiv.remove();
- }
- });
-
- //Calculates selection div size and position + which files are selected
- MAIN_BOX_EL.addEventListener('mousemove', async(e) => {
- const POSITION = new Point(e.pageX - mainBoxBounds.left, e.pageY - mainBoxBounds.top);
- if(isSelecting) {
- const DIRECTION = await getSelectingDivDirection(selectingOrigin, POSITION);
- let top, right, bottom, left,
- height = Math.abs(selectingOrigin.y - POSITION.y),
- width = Math.abs(selectingOrigin.x - POSITION.x);
-
- switch(DIRECTION.y) {
- case 0:
- top = selectingOrigin.y;
- bottom = mainBoxBounds.height - selectingOrigin.y;
- break;
- case -1:
- bottom = mainBoxBounds.height - selectingOrigin.y;
- top = 'auto';
- break;
- case 1:
- bottom = 'auto';
- top = selectingOrigin.y;
- break;
- }
-
- switch(DIRECTION.x) {
- case 0:
- left = selectingOrigin.x;
- right = mainBoxBounds.width - selectingOrigin.x;
- break;
- case -1:
- left = 'auto';
- right = mainBoxBounds.width - selectingOrigin.x;
- break;
- case 1:
- left = selectingOrigin.x;
- right = 'auto';
- break;
- }
-
- //Updating selecting div bounds manually so we don't call getBoundingClientRect each mousemove
- selectingDivBounds.bottom = bottom == 'auto' ? mainBoxBounds.height - (top as number) - height : bottom as number;
- selectingDivBounds.top = top == 'auto' ? mainBoxBounds.height - (bottom as number) - height : top as number;
- selectingDivBounds.left = left == 'auto' ? mainBoxBounds.width - (right as number) - width : left as number;
- selectingDivBounds.right = right == 'auto' ? mainBoxBounds.width - (left as number) - width : right as number;
- selectingDivBounds.width = width;
- selectingDivBounds.height = height;
-
- selectingDiv.style.inset = top + (top == 'auto' ? ' ' : 'px ') + right + (right == 'auto' ? ' ' : 'px ') +
- bottom + (bottom == 'auto' ? ' ' : 'px ') + left + (left == 'auto' ? ' ' : 'px ');
- selectingDiv.style.height = height + 'px';
- selectingDiv.style.width = width + 'px';
-
- const FILES = MAIN_BOX_EL.getElementsByClassName('file');
-
- for(let x = 0; x < FILES.length; x++) {
- const CURRENT = FILES[x] as HTMLElement;
- //This should be fine since this function is async
- const BOUNDS = CURRENT.getBoundingClientRect();
-
- //If overlaps
- if(!(BOUNDS.right - mainBoxBounds.left < selectingDivBounds.left ||
- BOUNDS.left - mainBoxBounds.left > mainBoxBounds.width - selectingDivBounds.right ||
- BOUNDS.bottom - mainBoxBounds.top < selectingDivBounds.top ||
- BOUNDS.top - mainBoxBounds.top > mainBoxBounds.height - selectingDivBounds.bottom) &&
- isSelecting)
- {
- if(!CURRENT.classList.contains('selected')) CURRENT.classList.add('selected');
- selected++;
- } else {
- if(CURRENT.classList.contains('selected')) {
- CURRENT.classList.remove('selected');
- selected--;
- }
- }
- }
- if(selected == 0) unselectAllSelected();
- }
- });
-
- //Updates main box bounds on resize
- MAIN_BOX_EL.addEventListener('resize', (e) => {
- mainBoxBounds = MAIN_BOX_EL.getBoundingClientRect();
- });
-
- const selectShortcut = async (e: KeyboardEvent) => {
- // Ignore keyboard shortcuts for select files if input element has focus.
- if (document.activeElement.tagName === 'INPUT') return;
-
- const hideHiddenFiles = (await Storage.get('preference'))?.hideHiddenFiles ?? true;
-
- const keyHandlers: {
- [key: string]: (e: KeyboardEvent, hideHiddenFiles: boolean) => void;
- } = {
- ArrowRight: arrowRightHandler,
- ArrowLeft: arrowLeftHandler,
- ArrowDown: arrowDownHandler,
- ArrowUp: arrowUpHandler,
- };
-
- if (!e.altKey && keyHandlers[e.key]) {
- if (!document.contains(latestSelected)) {
- await selectFirstFile();
- return;
- }
-
- keyHandlers[e.key](e, hideHiddenFiles);
- }
- };
- document.addEventListener('keydown', selectShortcut);
+ //Saving the main box element in a variable, better than calling MAIN_BOX_ELEMENT() everytime
+ const MAIN_BOX_EL: HTMLElement = MAIN_BOX_ELEMENT();
+ mainBoxBounds = MAIN_BOX_EL.getBoundingClientRect();
+
+ MAIN_BOX_EL.addEventListener("click", (e) => {
+ if (
+ !(e.target as HTMLElement).className.split(" ").some((c) => /file/.test(c)) &&
+ //If user doesn't drag the mouse
+ e.pageX - mainBoxBounds.left === selectingOrigin.x &&
+ e.pageY - mainBoxBounds.top === selectingOrigin.y
+ ) {
+ unselectAllSelected();
+ latestSelected = null;
+ latestShiftSelected = null;
+ }
+ let fileTarget = e.target as HTMLElement;
+ while (fileTarget?.classList && !fileTarget.classList.contains("file")) fileTarget = fileTarget.parentNode as HTMLElement;
+ if (fileTarget.id === "workspace" || !fileTarget?.classList?.contains("file")) return;
+
+ Select(fileTarget, e.ctrlKey, e.shiftKey);
+ });
+
+ MAIN_BOX_EL.addEventListener("mousedown", (e) => {
+ /// SelectInit is called async, therefore bounds come before full element calculation.
+ /// Here is to avoid wrong div placement, but we can find a better way without calling getBoundingClientRect each mousedown
+ mainBoxBounds = MAIN_BOX_EL.getBoundingClientRect();
+ //Start selection
+ isSelecting = true;
+ //Save selection starting point
+ selectingOrigin = new Point(e.pageX - mainBoxBounds.left, e.pageY - mainBoxBounds.top);
+ if (!(e.target as HTMLElement).className.split(" ").some((c) => /file/.test(c))) {
+ MAIN_BOX_EL.prepend(selectingDiv);
+ selectingDiv.style.inset = `${selectingOrigin.y}px auto ${selectingOrigin.x}px auto`;
+ selectingDiv.style.width = selectingDiv.style.height = "auto";
+ }
+ });
+
+ //Check when mouse is released. On window to catch events outside of main box
+ window.addEventListener("mouseup", (e) => {
+ if (isSelecting) {
+ isSelecting = false;
+
+ selectingDiv.style.inset = selectingDiv.style.width = selectingDiv.style.height = "auto";
+
+ selectingDiv.remove();
+ }
+ });
+
+ //Calculates selection div size and position + which files are selected
+ MAIN_BOX_EL.addEventListener("mousemove", async (e) => {
+ const POSITION = new Point(e.pageX - mainBoxBounds.left, e.pageY - mainBoxBounds.top);
+ if (isSelecting) {
+ const DIRECTION = await getSelectingDivDirection(selectingOrigin, POSITION);
+ let top: number | string;
+ let right: number | string;
+ let bottom: number | string;
+ let left: number | string;
+ const height = Math.abs(selectingOrigin.y - POSITION.y);
+ const width = Math.abs(selectingOrigin.x - POSITION.x);
+
+ switch (DIRECTION.y) {
+ case 0:
+ top = selectingOrigin.y;
+ bottom = mainBoxBounds.height - selectingOrigin.y;
+ break;
+ case -1:
+ bottom = mainBoxBounds.height - selectingOrigin.y;
+ top = "auto";
+ break;
+ case 1:
+ bottom = "auto";
+ top = selectingOrigin.y;
+ break;
+ }
+
+ switch (DIRECTION.x) {
+ case 0:
+ left = selectingOrigin.x;
+ right = mainBoxBounds.width - selectingOrigin.x;
+ break;
+ case -1:
+ left = "auto";
+ right = mainBoxBounds.width - selectingOrigin.x;
+ break;
+ case 1:
+ left = selectingOrigin.x;
+ right = "auto";
+ break;
+ }
+
+ //Updating selecting div bounds manually so we don't call getBoundingClientRect each mousemove
+ selectingDivBounds.bottom = bottom === "auto" ? mainBoxBounds.height - (top as number) - height : (bottom as number);
+ selectingDivBounds.top = top === "auto" ? mainBoxBounds.height - (bottom as number) - height : (top as number);
+ selectingDivBounds.left = left === "auto" ? mainBoxBounds.width - (right as number) - width : (left as number);
+ selectingDivBounds.right = right === "auto" ? mainBoxBounds.width - (left as number) - width : (right as number);
+ selectingDivBounds.width = width;
+ selectingDivBounds.height = height;
+
+ selectingDiv.style.inset =
+ top +
+ (top === "auto" ? " " : "px ") +
+ right +
+ (right === "auto" ? " " : "px ") +
+ bottom +
+ (bottom === "auto" ? " " : "px ") +
+ left +
+ (left === "auto" ? " " : "px ");
+ selectingDiv.style.height = `${height}px`;
+ selectingDiv.style.width = `${width}px`;
+
+ const FILES = MAIN_BOX_EL.getElementsByClassName("file");
+
+ for (let x = 0; x < FILES.length; x++) {
+ const CURRENT = FILES[x] as HTMLElement;
+ //This should be fine since this function is async
+ const BOUNDS = CURRENT.getBoundingClientRect();
+
+ //If overlaps
+ if (
+ !(
+ BOUNDS.right - mainBoxBounds.left < selectingDivBounds.left ||
+ BOUNDS.left - mainBoxBounds.left > mainBoxBounds.width - selectingDivBounds.right ||
+ BOUNDS.bottom - mainBoxBounds.top < selectingDivBounds.top ||
+ BOUNDS.top - mainBoxBounds.top > mainBoxBounds.height - selectingDivBounds.bottom
+ ) &&
+ isSelecting
+ ) {
+ if (!CURRENT.classList.contains("selected")) CURRENT.classList.add("selected");
+ selected++;
+ } else {
+ if (CURRENT.classList.contains("selected")) {
+ CURRENT.classList.remove("selected");
+ selected--;
+ }
+ }
+ }
+ if (selected === 0) unselectAllSelected();
+ }
+ });
+
+ //Updates main box bounds on resize
+ MAIN_BOX_EL.addEventListener("resize", (e) => {
+ mainBoxBounds = MAIN_BOX_EL.getBoundingClientRect();
+ });
+
+ const selectShortcut = async (e: KeyboardEvent) => {
+ // Ignore keyboard shortcuts for select files if input element has focus.
+ if (document.activeElement.tagName === "INPUT") return;
+
+ const hideHiddenFiles = (await Storage.get("preference"))?.hideHiddenFiles ?? true;
+
+ const keyHandlers: {
+ [key: string]: (e: KeyboardEvent, hideHiddenFiles: boolean) => void;
+ } = {
+ ArrowRight: arrowRightHandler,
+ ArrowLeft: arrowLeftHandler,
+ ArrowDown: arrowDownHandler,
+ ArrowUp: arrowUpHandler,
+ };
+
+ if (!e.altKey && keyHandlers[e.key]) {
+ if (!document.contains(latestSelected)) {
+ await selectFirstFile();
+ return;
+ }
+
+ keyHandlers[e.key](e, hideHiddenFiles);
+ }
+ };
+ document.addEventListener("keydown", selectShortcut);
};
/**
- *
+ *
* @param origin - mouse drag selection origin point
* @param last - last mouse position
* @returns {Promise} - direction based on coord deltas, x and y can be either -1, 0 or 1 indicating the direction relative to point 0,0
*/
-const getSelectingDivDirection = async(origin: Point, last: Point): Promise => {
- const delta = origin.delta(last);
+const getSelectingDivDirection = async (origin: Point, last: Point): Promise => {
+ const delta = origin.delta(last);
- return { x: delta.x == 0 ? 0 : delta.x > 0 ? -1 : 1,
- y: delta.y == 0 ? 0 : delta.y > 0 ? -1 : 1 };
-}
+ return {
+ x: delta.x === 0 ? 0 : delta.x > 0 ? -1 : 1,
+ y: delta.y === 0 ? 0 : delta.y > 0 ? -1 : 1,
+ };
+};
/**
* Unselect all selected file grids.
* @returns {void}
*/
const unselectAllSelected = (): void => {
- document.querySelectorAll('.selected').forEach((element) => element.classList.remove('selected'));
- ChangeSelectedEvent();
- return;
+ document.querySelectorAll(".selected").forEach((element) => element.classList.remove("selected"));
+ ChangeSelectedEvent();
+ return;
};
/**
@@ -319,146 +343,146 @@ const unselectAllSelected = (): void => {
* @returns {NodeListOf}
*/
const getSelected = (): NodeListOf => {
- return document.querySelectorAll('.selected');
+ return document.querySelectorAll(".selected");
};
const arrowRightHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
- if (latestShiftSelected && elementIndex(latestShiftSelected) < elementIndex(latestSelected)) {
- latestShiftSelected = latestSelected;
- }
- e.preventDefault();
- let nextSibling = (e.shiftKey ? latestShiftSelected.nextSibling : latestSelected.nextSibling) as HTMLElement;
- if (hideHiddenFiles) {
- while (nextSibling && nextSibling.dataset.isHidden === 'true') {
- nextSibling = nextSibling.nextSibling as HTMLElement;
- }
- }
- if (elementClassNameContains(nextSibling, /file/)) {
- ensureElementInViewPort(nextSibling);
- unselectAllSelected();
- if (e.shiftKey) {
- let start = false;
- for (const sibling of latestSelected.parentNode.children) {
- if (start || sibling === nextSibling || sibling === latestSelected) {
- if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === 'true')) sibling.classList.add('selected');
- }
- if (sibling === latestSelected) start = true;
- if (sibling === nextSibling) break;
- }
- latestShiftSelected = nextSibling;
- } else {
- latestSelected.classList.remove('selected');
- latestSelected = nextSibling;
- nextSibling.classList.add('selected');
- }
- ChangeSelectedEvent();
- }
+ if (latestShiftSelected && elementIndex(latestShiftSelected) < elementIndex(latestSelected)) {
+ latestShiftSelected = latestSelected;
+ }
+ e.preventDefault();
+ let nextSibling = (e.shiftKey ? latestShiftSelected.nextSibling : latestSelected.nextSibling) as HTMLElement;
+ if (hideHiddenFiles) {
+ while (nextSibling && nextSibling.dataset.isHidden === "true") {
+ nextSibling = nextSibling.nextSibling as HTMLElement;
+ }
+ }
+ if (elementClassNameContains(nextSibling, /file/)) {
+ ensureElementInViewPort(nextSibling);
+ unselectAllSelected();
+ if (e.shiftKey) {
+ let start = false;
+ for (const sibling of latestSelected.parentNode.children) {
+ if (start || sibling === nextSibling || sibling === latestSelected) {
+ if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === "true")) sibling.classList.add("selected");
+ }
+ if (sibling === latestSelected) start = true;
+ if (sibling === nextSibling) break;
+ }
+ latestShiftSelected = nextSibling;
+ } else {
+ latestSelected.classList.remove("selected");
+ latestSelected = nextSibling;
+ nextSibling.classList.add("selected");
+ }
+ ChangeSelectedEvent();
+ }
};
const arrowLeftHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
- if (latestShiftSelected && elementIndex(latestShiftSelected) > elementIndex(latestSelected)) latestShiftSelected = latestSelected;
-
- e.preventDefault();
- let previousSibling = (e.shiftKey ? latestShiftSelected.previousSibling : latestSelected.previousSibling) as HTMLElement;
- if (hideHiddenFiles) {
- while (previousSibling && previousSibling.dataset.isHidden === 'true') {
- previousSibling = previousSibling.previousSibling as HTMLElement;
- }
- }
- if (elementClassNameContains(previousSibling, /file/)) {
- ensureElementInViewPort(previousSibling);
- let start = false;
- unselectAllSelected();
- if (e.shiftKey) {
- for (const sibling of latestSelected.parentNode.children) {
- if (start || sibling === previousSibling || sibling === latestSelected) {
- if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === 'true')) sibling.classList.add('selected');
- }
- if (sibling === previousSibling) start = true;
- if (sibling === latestSelected) break;
- }
- latestShiftSelected = previousSibling;
- } else {
- latestSelected.classList.remove('selected');
- latestSelected = previousSibling;
- previousSibling.classList.add('selected');
- }
- ChangeSelectedEvent();
- }
+ if (latestShiftSelected && elementIndex(latestShiftSelected) > elementIndex(latestSelected)) latestShiftSelected = latestSelected;
+
+ e.preventDefault();
+ let previousSibling = (e.shiftKey ? latestShiftSelected.previousSibling : latestSelected.previousSibling) as HTMLElement;
+ if (hideHiddenFiles) {
+ while (previousSibling && previousSibling.dataset.isHidden === "true") {
+ previousSibling = previousSibling.previousSibling as HTMLElement;
+ }
+ }
+ if (elementClassNameContains(previousSibling, /file/)) {
+ ensureElementInViewPort(previousSibling);
+ let start = false;
+ unselectAllSelected();
+ if (e.shiftKey) {
+ for (const sibling of latestSelected.parentNode.children) {
+ if (start || sibling === previousSibling || sibling === latestSelected) {
+ if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === "true")) sibling.classList.add("selected");
+ }
+ if (sibling === previousSibling) start = true;
+ if (sibling === latestSelected) break;
+ }
+ latestShiftSelected = previousSibling;
+ } else {
+ latestSelected.classList.remove("selected");
+ latestSelected = previousSibling;
+ previousSibling.classList.add("selected");
+ }
+ ChangeSelectedEvent();
+ }
};
const arrowDownHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
- if (latestShiftSelected && elementIndex(latestShiftSelected) < elementIndex(latestSelected)) latestShiftSelected = latestSelected;
-
- e.preventDefault();
- const totalGridInArrow = Math.floor(
- (latestSelected.parentNode as HTMLElement).offsetWidth /
- (latestSelected.offsetWidth + parseInt(getComputedStyle(latestSelected).marginLeft) * 2)
- ); // Calculate the total of grids in arrow
- const siblings = latestSelected.parentNode.children;
- let elementBelow = siblings[Array.from(siblings).indexOf(e.shiftKey ? latestShiftSelected : latestSelected) + totalGridInArrow] as HTMLElement;
- if (hideHiddenFiles) {
- while (elementBelow && elementBelow.dataset.isHidden === 'true') {
- elementBelow = siblings[Array.from(siblings).indexOf(elementBelow) + totalGridInArrow] as HTMLElement;
- }
- }
- if (elementClassNameContains(elementBelow, /file/)) {
- ensureElementInViewPort(elementBelow);
- let start = false;
- unselectAllSelected();
- if (e.shiftKey) {
- for (const sibling of latestSelected.parentNode.children) {
- if (start || sibling === elementBelow || sibling === latestSelected) {
- if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === 'true')) sibling.classList.add('selected');
- }
- if (sibling === latestSelected) start = true;
- if (sibling === elementBelow) break;
- }
- latestShiftSelected = elementBelow;
- } else {
- latestSelected.classList.remove('selected');
- latestSelected = elementBelow;
- elementBelow.classList.add('selected');
- }
- ChangeSelectedEvent();
- }
+ if (latestShiftSelected && elementIndex(latestShiftSelected) < elementIndex(latestSelected)) latestShiftSelected = latestSelected;
+
+ e.preventDefault();
+ const totalGridInArrow = Math.floor(
+ (latestSelected.parentNode as HTMLElement).offsetWidth /
+ (latestSelected.offsetWidth + Number.parseInt(getComputedStyle(latestSelected).marginLeft) * 2),
+ ); // Calculate the total of grids in arrow
+ const siblings = latestSelected.parentNode.children;
+ let elementBelow = siblings[Array.from(siblings).indexOf(e.shiftKey ? latestShiftSelected : latestSelected) + totalGridInArrow] as HTMLElement;
+ if (hideHiddenFiles) {
+ while (elementBelow && elementBelow.dataset.isHidden === "true") {
+ elementBelow = siblings[Array.from(siblings).indexOf(elementBelow) + totalGridInArrow] as HTMLElement;
+ }
+ }
+ if (elementClassNameContains(elementBelow, /file/)) {
+ ensureElementInViewPort(elementBelow);
+ let start = false;
+ unselectAllSelected();
+ if (e.shiftKey) {
+ for (const sibling of latestSelected.parentNode.children) {
+ if (start || sibling === elementBelow || sibling === latestSelected) {
+ if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === "true")) sibling.classList.add("selected");
+ }
+ if (sibling === latestSelected) start = true;
+ if (sibling === elementBelow) break;
+ }
+ latestShiftSelected = elementBelow;
+ } else {
+ latestSelected.classList.remove("selected");
+ latestSelected = elementBelow;
+ elementBelow.classList.add("selected");
+ }
+ ChangeSelectedEvent();
+ }
};
const arrowUpHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
- if (latestShiftSelected && elementIndex(latestShiftSelected) > elementIndex(latestSelected)) latestShiftSelected = latestSelected;
-
- e.preventDefault();
- const totalGridInArrow = Math.floor(
- (latestSelected.parentNode as HTMLElement).offsetWidth /
- (latestSelected.offsetWidth + parseInt(getComputedStyle(latestSelected).marginLeft) * 2)
- ); // Calculate the total of grids in arrow
- const siblings = latestSelected.parentNode.children;
- let elementAbove = siblings[Array.from(siblings).indexOf(e.shiftKey ? latestShiftSelected : latestSelected) - totalGridInArrow] as HTMLElement;
- if (hideHiddenFiles) {
- while (elementAbove && elementAbove.dataset.isHidden === 'true') {
- elementAbove = siblings[Array.from(siblings).indexOf(elementAbove) - totalGridInArrow] as HTMLElement;
- }
- }
- if (elementClassNameContains(elementAbove, /file/)) {
- ensureElementInViewPort(elementAbove);
- let start = false;
- unselectAllSelected();
- if (e.shiftKey) {
- for (const sibling of latestSelected.parentNode.children) {
- if (start || sibling === elementAbove || sibling === latestSelected) {
- if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === 'true')) sibling.classList.add('selected');
- }
- if (sibling === elementAbove) start = true;
- if (sibling === latestSelected) break;
- }
- latestShiftSelected = elementAbove;
- } else {
- latestSelected.classList.remove('selected');
- latestSelected = elementAbove;
- elementAbove.classList.add('selected');
- }
- ChangeSelectedEvent();
- }
+ if (latestShiftSelected && elementIndex(latestShiftSelected) > elementIndex(latestSelected)) latestShiftSelected = latestSelected;
+
+ e.preventDefault();
+ const totalGridInArrow = Math.floor(
+ (latestSelected.parentNode as HTMLElement).offsetWidth /
+ (latestSelected.offsetWidth + Number.parseInt(getComputedStyle(latestSelected).marginLeft) * 2),
+ ); // Calculate the total of grids in arrow
+ const siblings = latestSelected.parentNode.children;
+ let elementAbove = siblings[Array.from(siblings).indexOf(e.shiftKey ? latestShiftSelected : latestSelected) - totalGridInArrow] as HTMLElement;
+ if (hideHiddenFiles) {
+ while (elementAbove && elementAbove.dataset.isHidden === "true") {
+ elementAbove = siblings[Array.from(siblings).indexOf(elementAbove) - totalGridInArrow] as HTMLElement;
+ }
+ }
+ if (elementClassNameContains(elementAbove, /file/)) {
+ ensureElementInViewPort(elementAbove);
+ let start = false;
+ unselectAllSelected();
+ if (e.shiftKey) {
+ for (const sibling of latestSelected.parentNode.children) {
+ if (start || sibling === elementAbove || sibling === latestSelected) {
+ if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === "true")) sibling.classList.add("selected");
+ }
+ if (sibling === elementAbove) start = true;
+ if (sibling === latestSelected) break;
+ }
+ latestShiftSelected = elementAbove;
+ } else {
+ latestSelected.classList.remove("selected");
+ latestSelected = elementAbove;
+ elementAbove.classList.add("selected");
+ }
+ ChangeSelectedEvent();
+ }
};
export { Select, SelectInit, getSelected, ChangeSelectedEvent, unselectAllSelected };
diff --git a/src/Components/Files/File Operation/trash.ts b/src/Components/Files/File Operation/trash.ts
index a747853b..8a9ceff6 100644
--- a/src/Components/Files/File Operation/trash.ts
+++ b/src/Components/Files/File Operation/trash.ts
@@ -1,10 +1,10 @@
-import { OperationLog } from '../../Functions/log';
-import PromptError from '../../Prompt/error';
-import OS from '../../../Service/platform';
-import { DeleteFiles, PurgeFiles, RestoreFiles, RestoreFile as RestoreFileAPI } from '../../../Service/trash';
-import OperationAPI from '../../../Service/operation';
-import ConfirmDialog from '../../Prompt/confirm';
-import { reload } from '../../Layout/windowManager';
+import OperationAPI from "../../../Service/operation";
+import OS from "../../../Service/platform";
+import { DeleteFiles, PurgeFiles, RestoreFile as RestoreFileAPI, RestoreFiles } from "../../../Service/trash";
+import { OperationLog } from "../../Functions/log";
+import { reload } from "../../Layout/windowManager";
+import ConfirmDialog from "../../Prompt/confirm";
+import PromptError from "../../Prompt/error";
let platform: string;
/**
@@ -13,29 +13,29 @@ let platform: string;
* @returns {Promise}
*/
const Restore = async (filePaths: string[]): Promise => {
- if (!platform) platform = await OS();
- if (platform === 'darwin') return;
+ if (!platform) platform = await OS();
+ if (platform === "darwin") return;
- const result = await RestoreFiles(filePaths);
- if (result.request_confirmation) {
- const confirm = await ConfirmDialog('Something went wrong when trying to restore the file/dir', result.message, 'Yes');
- if (!confirm) return;
- RestoreFiles(filePaths, true);
- }
- reload();
+ const result = await RestoreFiles(filePaths);
+ if (result.request_confirmation) {
+ const confirm = await ConfirmDialog("Something went wrong when trying to restore the file/dir", result.message, "Yes");
+ if (!confirm) return;
+ RestoreFiles(filePaths, true);
+ }
+ reload();
};
const Purge = async (filePaths: string[]): Promise => {
- if (!platform) platform = await OS();
- if (platform === 'darwin') return;
- const confirm = await ConfirmDialog(
- 'Permanently delete file',
- "Are you sure to permanently delete these files/dirs? This can't be undone.",
- 'Yes'
- );
- if (!confirm) return;
- PurgeFiles(filePaths);
- reload();
+ if (!platform) platform = await OS();
+ if (platform === "darwin") return;
+ const confirm = await ConfirmDialog(
+ "Permanently delete file",
+ "Are you sure to permanently delete these files/dirs? This can't be undone.",
+ "Yes",
+ );
+ if (!confirm) return;
+ PurgeFiles(filePaths);
+ reload();
};
/**
@@ -44,15 +44,15 @@ const Purge = async (filePaths: string[]): Promise => {
* @returns {Promise}
*/
const Trash = async (filePaths: string[]): Promise => {
- if (!platform) platform = await OS();
- try {
- DeleteFiles(filePaths);
- } catch (err) {
- PromptError('Failed to delete files/dirs', `Failed to move ` + filePaths.join(', ') + ` to trash. [${err}]`);
- }
- if (platform !== 'darwin') {
- OperationLog('delete', filePaths);
- }
+ if (!platform) platform = await OS();
+ try {
+ DeleteFiles(filePaths);
+ } catch (err) {
+ PromptError("Failed to delete files/dirs", `Failed to move ${filePaths.join(", ")} to trash. [${err}]`);
+ }
+ if (platform !== "darwin") {
+ OperationLog("delete", filePaths);
+ }
};
/**
@@ -62,9 +62,9 @@ const Trash = async (filePaths: string[]): Promise => {
* @returns {Promise}
*/
const RestoreFile = async (original_parent: string, basename: string): Promise => {
- if (!platform) platform = await OS();
- if (platform === 'darwin') return;
- await RestoreFileAPI(original_parent, basename);
+ if (!platform) platform = await OS();
+ if (platform === "darwin") return;
+ await RestoreFileAPI(original_parent, basename);
};
/**
@@ -73,17 +73,17 @@ const RestoreFile = async (original_parent: string, basename: string): Promise => {
- const confirm = await ConfirmDialog(
- 'Permanently delete file',
- filePaths.length > 1
- ? "Are you sure to permanently delete these files/dirs? This can't be undone."
- : "Are you sure to permanently delete this file/dir? This can't be undone.",
- 'Yes'
- );
- if (!confirm) return;
- for (const filePath of filePaths) {
- await new OperationAPI(filePath).unlink();
- }
+ const confirm = await ConfirmDialog(
+ "Permanently delete file",
+ filePaths.length > 1
+ ? "Are you sure to permanently delete these files/dirs? This can't be undone."
+ : "Are you sure to permanently delete this file/dir? This can't be undone.",
+ "Yes",
+ );
+ if (!confirm) return;
+ for (const filePath of filePaths) {
+ await new OperationAPI(filePath).unlink();
+ }
};
export { Trash, PermanentDelete, Restore, Purge, RestoreFile };
diff --git a/src/Components/Files/File Operation/undo.ts b/src/Components/Files/File Operation/undo.ts
index 6d1ee1f3..48846905 100644
--- a/src/Components/Files/File Operation/undo.ts
+++ b/src/Components/Files/File Operation/undo.ts
@@ -1,70 +1,69 @@
-import windowName from '../../../Service/window';
-import OperationAPI from '../../../Service/operation';
-import Storage from '../../../Service/storage';
-import joinPath from '../../Functions/path/joinPath';
-import getBasename from '../../Functions/path/basename';
-import FileAPI from '../../../Service/files';
-import ConfirmDialog from '../../Prompt/confirm';
-import DirectoryAPI from '../../../Service/directory';
-import { RestoreFile } from './trash';
-import getDirname from '../../Functions/path/dirname';
-import NormalizeSlash from '../../Functions/path/normalizeSlash';
+import DirectoryAPI from "../../../Service/directory";
+import FileAPI from "../../../Service/files";
+import OperationAPI from "../../../Service/operation";
+import Storage from "../../../Service/storage";
+import windowName from "../../../Service/window";
+import getBasename from "../../Functions/path/basename";
+import getDirname from "../../Functions/path/dirname";
+import joinPath from "../../Functions/path/joinPath";
+import NormalizeSlash from "../../Functions/path/normalizeSlash";
+import ConfirmDialog from "../../Prompt/confirm";
+import { RestoreFile } from "./trash";
/**
* Undo the latest operation
* @returns {Promise}
*/
const Undo = async (): Promise => {
- const operationLogs = await Storage.get(`operations-${windowName}`);
- const latestOperation = operationLogs?.operations[operationLogs?.currentIndex];
- switch (latestOperation?.operationType) {
- case 'copy':
- for (const source of latestOperation.sources) {
- const filename = joinPath(latestOperation.destination, getBasename(source));
- const filenameWithCopySuffix = `${filename
- .split('.')
- .splice(0, filename.split('.').length - 1)
- .join('.')} - Copy.${filename.split('.').splice(filename.split('.').length - 1)}`;
- const copiedFile = (await new FileAPI(filenameWithCopySuffix).exists()) ? filenameWithCopySuffix : filename;
- new OperationAPI(copiedFile).unlink();
- }
- break;
- case 'cut':
- for (const source of latestOperation.sources) {
- const dest = joinPath(latestOperation.destination, getBasename(source));
- if (await new DirectoryAPI(source).exists()) {
- if (
- !(await ConfirmDialog(
- 'Target file exists',
- 'Target directory with the same file/dir name exists, do you want to overwrite it?',
- 'No'
- ))
- )
- return;
- else {
- await new OperationAPI(source).unlink();
- }
- }
- await new OperationAPI(dest, source).rename();
- break;
- }
- break;
- case 'newfile':
- new OperationAPI(latestOperation.destination).unlink();
- break;
- case 'newfolder':
- new OperationAPI(latestOperation.destination).unlink();
- break;
- case 'delete':
- for (const source of latestOperation.sources) {
- RestoreFile(NormalizeSlash(getDirname(source)), NormalizeSlash(getBasename(source)));
- }
- break;
- case 'rename':
- await new OperationAPI(latestOperation.destination, latestOperation.sources).rename();
- break;
- }
- if (operationLogs.currentIndex > -1) operationLogs.currentIndex -= 1;
- Storage.set(`operations-${windowName}`, operationLogs);
+ const operationLogs = await Storage.get(`operations-${windowName}`);
+ const latestOperation = operationLogs?.operations[operationLogs?.currentIndex];
+ switch (latestOperation?.operationType) {
+ case "copy":
+ for (const source of latestOperation.sources) {
+ const filename = joinPath(latestOperation.destination, getBasename(source));
+ const filenameWithCopySuffix = `${filename
+ .split(".")
+ .splice(0, filename.split(".").length - 1)
+ .join(".")} - Copy.${filename.split(".").splice(filename.split(".").length - 1)}`;
+ const copiedFile = (await new FileAPI(filenameWithCopySuffix).exists()) ? filenameWithCopySuffix : filename;
+ new OperationAPI(copiedFile).unlink();
+ }
+ break;
+ case "cut":
+ for (const source of latestOperation.sources) {
+ const dest = joinPath(latestOperation.destination, getBasename(source));
+ if (await new DirectoryAPI(source).exists()) {
+ if (
+ !(await ConfirmDialog(
+ "Target file exists",
+ "Target directory with the same file/dir name exists, do you want to overwrite it?",
+ "No",
+ ))
+ )
+ return;
+
+ await new OperationAPI(source).unlink();
+ }
+ await new OperationAPI(dest, source).rename();
+ break;
+ }
+ break;
+ case "newfile":
+ new OperationAPI(latestOperation.destination).unlink();
+ break;
+ case "newfolder":
+ new OperationAPI(latestOperation.destination).unlink();
+ break;
+ case "delete":
+ for (const source of latestOperation.sources) {
+ RestoreFile(NormalizeSlash(getDirname(source)), NormalizeSlash(getBasename(source)));
+ }
+ break;
+ case "rename":
+ await new OperationAPI(latestOperation.destination, latestOperation.sources).rename();
+ break;
+ }
+ if (operationLogs.currentIndex > -1) operationLogs.currentIndex -= 1;
+ Storage.set(`operations-${windowName}`, operationLogs);
};
export default Undo;
diff --git a/src/Components/Files/File Preview/preview.ts b/src/Components/Files/File Preview/preview.ts
index 54a3bc82..7b759877 100644
--- a/src/Components/Files/File Preview/preview.ts
+++ b/src/Components/Files/File Preview/preview.ts
@@ -1,33 +1,33 @@
-import PromptError from '../../Prompt/error';
-import { HTML_TYPES, IMAGE_TYPES, VIDEO_TYPES, PLAINTEXT_TYPES, MARKDOWN_TYPES } from '../../../Config/file.config';
-import getBasename from '../../Functions/path/basename';
-import xlsx from 'xlsx';
-import FileAPI from '../../../Service/files';
-import { eURLify, URLify } from '../../Functions/urlify';
-import hljs from 'highlight.js';
-import ConfirmDialog from '../../Prompt/confirm';
-import { marked } from 'marked';
-import getDirname from '../../Functions/path/dirname';
-import isTauri from '../../../Util/is-tauri';
-import { GET_WORKSPACE_ELEMENT } from '../../../Util/constants';
+import hljs from "highlight.js";
+import { marked } from "marked";
+import xlsx from "xlsx";
+import { HTML_TYPES, IMAGE_TYPES, MARKDOWN_TYPES, PLAINTEXT_TYPES, VIDEO_TYPES } from "../../../Config/file.config";
+import FileAPI from "../../../Service/files";
+import { GET_WORKSPACE_ELEMENT } from "../../../Util/constants";
+import isTauri from "../../../Util/is-tauri";
+import getBasename from "../../Functions/path/basename";
+import getDirname from "../../Functions/path/dirname";
+import { URLify, eURLify } from "../../Functions/urlify";
+import ConfirmDialog from "../../Prompt/confirm";
+import PromptError from "../../Prompt/error";
const isValidURL = (text: string) => {
- let url;
- try {
- url = new URL(text);
- } catch (_) {
- return false;
- }
- return (url.protocol === 'http:' || url.protocol === 'https:') && url.hostname !== window.location.hostname;
+ let url: URL;
+ try {
+ url = new URL(text);
+ } catch (_) {
+ return false;
+ }
+ return (url.protocol === "http:" || url.protocol === "https:") && url.hostname !== window.location.hostname;
};
/**
* Close the preview file
* @returns {void}
*/
const closePreviewFile = (): void => {
- GET_WORKSPACE_ELEMENT(1).classList.remove('workspace-split');
- document.querySelectorAll('.preview').forEach((element) => element.parentNode.removeChild(element));
- document.querySelector('.main-box').style.overflowY = 'auto';
+ GET_WORKSPACE_ELEMENT(1).classList.remove("workspace-split");
+ document.querySelectorAll(".preview").forEach((element) => element.parentNode.removeChild(element));
+ document.querySelector(".main-box").style.overflowY = "auto";
};
/**
* Show preview file
@@ -35,18 +35,18 @@ const closePreviewFile = (): void => {
* @returns {void}
*/
const Preview = async (filePath: string): Promise => {
- if (!isTauri) {
- PromptError('Preview unavailable', 'Preview is currently unavailable on Web version');
- return;
- }
- closePreviewFile();
+ if (!isTauri) {
+ PromptError("Preview unavailable", "Preview is currently unavailable on Web version");
+ return;
+ }
+ closePreviewFile();
- const previewElement = document.createElement('div');
- previewElement.classList.add('preview');
+ const previewElement = document.createElement("div");
+ previewElement.classList.add("preview");
- const changePreview = (html: string) => {
- if (!html) return;
- previewElement.innerHTML = `
+ const changePreview = (html: string) => {
+ if (!html) return;
+ previewElement.innerHTML = `