Skip to content

Commit

Permalink
Merge branch 'master' into feat/extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
kimlimjustin committed Dec 9, 2021
2 parents 73fa528 + 6a63bcf commit ef81720
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 28 deletions.
29 changes: 10 additions & 19 deletions src/Components/Files/File Operation/select.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { isElementInViewport } from '../../Functions/lazyLoadingImage';
import { elementClassNameContains } from '../../Functions/elementClassNameContains';
import Storage from '../../../Api/storage';
import { UpdateInfo } from '../../Layout/infobar';
import FileAPI from '../../../Api/files';
import formatBytes from '../../Functions/filesize';
import Preview from '../File Preview/preview';
import { ensureElementInViewPort } from '../../Functions/viewport';

let latestSelected: HTMLElement;
let latestShiftSelected: HTMLElement;
Expand Down Expand Up @@ -71,15 +71,6 @@ const Select = (element: HTMLElement, ctrl: boolean, shift: boolean): void => {
ensureElementInViewPort(element);
};

/**
* Ensure an element in view port
* @param {HTMLElement} element - element you want to ensure
* @returns {void}
*/
const ensureElementInViewPort = (element: HTMLElement): void => {
if (!isElementInViewport(element)) element.scrollIntoView();
};

/**
* Select the first file there in case the latest selected file is not exist
* @returns {Promise<void>}
Expand Down Expand Up @@ -169,7 +160,7 @@ const arrowRightHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void =>
e.preventDefault();
let nextSibling = (e.shiftKey ? latestShiftSelected.nextSibling : latestSelected.nextSibling) as HTMLElement;
if (hideHiddenFiles) {
while (nextSibling && nextSibling.dataset.hiddenFile !== undefined) {
while (nextSibling && nextSibling.dataset.isHidden === 'true') {
nextSibling = nextSibling.nextSibling as HTMLElement;
}
}
Expand All @@ -180,7 +171,7 @@ const arrowRightHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void =>
let start = false;
for (const sibling of latestSelected.parentNode.children) {
if (start || sibling === nextSibling || sibling === latestSelected) {
if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.hiddenFile === 'true')) sibling.classList.add('selected');
if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === 'true')) sibling.classList.add('selected');
}
if (sibling === latestSelected) start = true;
if (sibling === nextSibling) break;
Expand All @@ -201,7 +192,7 @@ const arrowLeftHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
e.preventDefault();
let previousSibling = (e.shiftKey ? latestShiftSelected.previousSibling : latestSelected.previousSibling) as HTMLElement;
if (hideHiddenFiles) {
while (previousSibling && previousSibling.dataset.hiddenFile !== undefined) {
while (previousSibling && previousSibling.dataset.isHidden === 'true') {
previousSibling = previousSibling.previousSibling as HTMLElement;
}
}
Expand All @@ -212,7 +203,7 @@ const arrowLeftHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
if (e.shiftKey) {
for (const sibling of latestSelected.parentNode.children) {
if (start || sibling === previousSibling || sibling === latestSelected) {
if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.hiddenFile === 'true')) sibling.classList.add('selected');
if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === 'true')) sibling.classList.add('selected');
}
if (sibling === previousSibling) start = true;
if (sibling === latestSelected) break;
Expand All @@ -238,7 +229,7 @@ const arrowDownHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
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.hiddenFile !== undefined) {
while (elementBelow && elementBelow.dataset.isHidden === 'true') {
elementBelow = siblings[Array.from(siblings).indexOf(elementBelow) + totalGridInArrow] as HTMLElement;
}
}
Expand All @@ -249,7 +240,7 @@ const arrowDownHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
if (e.shiftKey) {
for (const sibling of latestSelected.parentNode.children) {
if (start || sibling === elementBelow || sibling === latestSelected) {
if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.hiddenFile === 'true')) sibling.classList.add('selected');
if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === 'true')) sibling.classList.add('selected');
}
if (sibling === latestSelected) start = true;
if (sibling === elementBelow) break;
Expand All @@ -275,7 +266,7 @@ const arrowUpHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
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.hiddenFile !== undefined) {
while (elementAbove && elementAbove.dataset.isHidden === 'true') {
elementAbove = siblings[Array.from(siblings).indexOf(elementAbove) - totalGridInArrow] as HTMLElement;
}
}
Expand All @@ -286,7 +277,7 @@ const arrowUpHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
if (e.shiftKey) {
for (const sibling of latestSelected.parentNode.children) {
if (start || sibling === elementAbove || sibling === latestSelected) {
if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.hiddenFile === 'true')) sibling.classList.add('selected');
if (!(hideHiddenFiles && (sibling as HTMLElement).dataset.isHidden === 'true')) sibling.classList.add('selected');
}
if (sibling === elementAbove) start = true;
if (sibling === latestSelected) break;
Expand All @@ -301,4 +292,4 @@ const arrowUpHandler = (e: KeyboardEvent, hideHiddenFiles: boolean): void => {
}
};

export { Select, SelectInit, getSelected, ChangeSelectedEvent };
export { Select, SelectInit, getSelected, ChangeSelectedEvent, unselectAllSelected };
6 changes: 3 additions & 3 deletions src/Components/Functions/lazyLoadingImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const FETCHED_ICONS: string[] = []; // Array of fetch icons
* @param {HTMLElement} el - Element to check
* @returns {boolean} if element in viewport
*/
export const isElementInViewport = (el: HTMLElement): boolean => {
const isOnImageViewport = (el: HTMLElement): boolean => {
const rect = el.getBoundingClientRect();
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
return (
Expand All @@ -25,7 +25,7 @@ export const isElementInViewport = (el: HTMLElement): boolean => {
export const LOAD_IMAGE = (): void => {
const images = document.querySelectorAll('img[data-src]');
images.forEach((image: HTMLImageElement) => {
if (isElementInViewport(image)) {
if (isOnImageViewport(image)) {
if (image.dataset.isImg === 'true') {
image.src = new FileAPI(image.dataset.src).readAsset();
} else {
Expand All @@ -46,7 +46,7 @@ const LAZY_LOAD_INIT = (): void => {
document.querySelector('.main-box').addEventListener('scroll', () => {
const images = document.querySelectorAll('img[data-src]');
images.forEach((image: HTMLImageElement) => {
if (isElementInViewport(image)) {
if (isOnImageViewport(image)) {
if (image.dataset.isImg === 'true') {
image.src = new FileAPI(image.dataset.src).readAsset();
} else {
Expand Down
25 changes: 25 additions & 0 deletions src/Components/Functions/viewport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Check if element in viewport
* @param {HTMLElement} el - Element to check
* @returns {boolean} if element in viewport
*/
const isElementInViewport = (el: HTMLElement): boolean => {
const rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
};

/**
* Ensure an element in view port
* @param {HTMLElement} element - element you want to ensure
* @returns {void}
*/
const ensureElementInViewPort = (element: HTMLElement): void => {
if (!isElementInViewport(element)) element.scrollIntoView({ block: 'center', behavior: 'smooth' });
};

export { ensureElementInViewPort };
20 changes: 15 additions & 5 deletions src/Components/Open/open.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { LOAD_IMAGE } from '../Functions/lazyLoadingImage';
import PromptError from '../Prompt/error';
import { UpdateInfo } from '../Layout/infobar';
import { processSearch, stopSearchingProcess } from '../Files/File Operation/search';
import Storage from '../../Api/storage';
let platform: string;
let directoryInfo: DirectoryAPI;
/**
Expand Down Expand Up @@ -156,9 +157,17 @@ const OpenDir = async (dir: string, reveal?: boolean, forceOpen = false): Promis
/**
* Open file/folder handler
* @param {any} e - event
* @returns {void}
* @returns {Promise<void>}
*/
const OpenHandler = (e: Event): void => {
const OpenHandler = async (e: MouseEvent): Promise<void> => {
const preference = await Storage.get('preference');
if (document.querySelector('#sidebar-nav').contains(e.target as HTMLElement)) {
if (e.detail === 1 && preference.clickToOpenSidebar === 'double') return;
} else if ((await focusingPath()) === 'xplorer://Home') {
if (e.detail === 1 && preference.clickToOpenHome !== 'single') return;
} else {
if (e.detail === 1 && preference.clickToOpenFile !== 'single') return;
}
let element = e.target as HTMLElement;
while (element?.dataset && !element.dataset.path) {
element = element.parentNode as HTMLElement;
Expand All @@ -178,10 +187,11 @@ const OpenHandler = (e: Event): void => {
};
/**
* Open directory/file listener initializer
* @returns {void}
* @returns {Promise<void>}
*/
const OpenInit = (): void => {
const OpenInit = async (): Promise<void> => {
const preference = await Storage.get('preference');
document.querySelector('#sidebar-nav').addEventListener('click', OpenHandler);
document.querySelector('#workspace').addEventListener('dblclick', OpenHandler);
document.querySelector('#workspace').addEventListener('click', OpenHandler);
};
export { OpenInit, OpenDir };
44 changes: 44 additions & 0 deletions src/Components/Setting/Preference/preference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const Preference = async (): Promise<void> => {
const automaticallyChangePreviewFile = _preference?.automaticallyChangePreviewFile ?? true;
const settingsMain = document.querySelector('.settings-main');
const on_startup = _preference?.on_startup ?? 'new';
const clickToOpenSidebar = _preference?.clickToOpenSidebar ?? 'single';
const clickToOpenHome = _preference?.clickToOpenHome ?? 'double';
const clickToOpenFile = _preference?.clickToOpenFile ?? 'double';

const appLanguage_i18n = await Translate('App Language');
const fileAndFolders_i18n = await Translate('Files and Folders');
Expand All @@ -31,6 +34,10 @@ const Preference = async (): Promise<void> => {
const on_startup_i18n = await Translate('On startup');
const detectDriveChange_i18n = await Translate('Detect Drive Change');
const automaticallyChangePreviewFile_i18n = await Translate('Automatically change preview file with selected file');
const clickToOpen_i18n = await Translate('Single/Double Click to open a file');
const clickToOpenSidebar_i18n = await Translate('Double click to open items under sidebar section');
const clickToOpenHome_i18n = await Translate('Double click to open items under home section');
const clickToOpenFile_i18n = await Translate('Double click to open files/folders');
const preferencePage = `<h3 class="settings-title">${appLanguage_i18n}</h3>
<select name="language">
${Object.keys(localesData.AVAILABLE_LOCALES)
Expand Down Expand Up @@ -78,6 +85,28 @@ const Preference = async (): Promise<void> => {
<span class="toggle-label">${automaticallyChangePreviewFile_i18n}</span>
</label>
</div>
<h3 class="settings-title">${clickToOpen_i18n}</h3>
<div class="toggle-box">
<label class="toggle">
<input type="checkbox" name="click-to-open-sidebar" ${clickToOpenSidebar === 'double' ? 'checked' : ''}>
<span class="toggle-slider"></span>
<span class="toggle-label">${clickToOpenSidebar_i18n}</span>
</label>
</div>
<div class="toggle-box">
<label class="toggle">
<input type="checkbox" name="click-to-open-home" ${clickToOpenHome === 'double' ? 'checked' : ''}>
<span class="toggle-slider"></span>
<span class="toggle-label">${clickToOpenHome_i18n}</span>
</label>
</div>
<div class="toggle-box">
<label class="toggle">
<input type="checkbox" name="click-to-open-file" ${clickToOpenFile === 'double' ? 'checked' : ''}>
<span class="toggle-slider"></span>
<span class="toggle-label">${clickToOpenFile_i18n}</span>
</label>
</div>
<h3 class="settings-title">${on_startup_i18n}</h3>
<select name="on_startup">
<option ${on_startup === 'new' ? 'selected' : ''} value="new">New tab</option>
Expand Down Expand Up @@ -123,6 +152,21 @@ const Preference = async (): Promise<void> => {
preference.automaticallyChangePreviewFile = event.target.checked;
Storage.set('preference', preference);
});
settingsMain.querySelector(`[name="click-to-open-sidebar"]`).addEventListener('change', async (event: Event & { target: HTMLInputElement }) => {
const preference = (await Storage.get('preference')) ?? {};
preference.clickToOpenSidebar = event.target.checked ? 'double' : 'single';
Storage.set('preference', preference);
});
settingsMain.querySelector(`[name="click-to-open-home"]`).addEventListener('change', async (event: Event & { target: HTMLInputElement }) => {
const preference = (await Storage.get('preference')) ?? {};
preference.clickToOpenHome = event.target.checked ? 'double' : 'single';
Storage.set('preference', preference);
});
settingsMain.querySelector(`[name="click-to-open-file"]`).addEventListener('change', async (event: Event & { target: HTMLInputElement }) => {
const preference = (await Storage.get('preference')) ?? {};
preference.clickToOpenFile = event.target.checked ? 'double' : 'single';
Storage.set('preference', preference);
});
settingsMain.querySelector(`[name="on_startup"]`).addEventListener('change', async (event: Event & { target: HTMLInputElement }) => {
const preference = (await Storage.get('preference')) ?? {};
preference.on_startup = event.target.value;
Expand Down
52 changes: 51 additions & 1 deletion src/Components/Shortcut/shortcut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import focusingPath from '../Functions/focusingPath';
import { OpenDir } from '../Open/open';
import getDirname from '../Functions/path/dirname';
import copyLocation from '../Files/File Operation/location';
import { ChangeSelectedEvent, getSelected } from '../Files/File Operation/select';
import { ChangeSelectedEvent, getSelected, Select, unselectAllSelected } from '../Files/File Operation/select';
import Pin from '../Files/File Operation/pin';
import New from '../Functions/new';
import { createNewWindow } from '../../Api/window';
Expand All @@ -24,6 +24,7 @@ import Redo from '../Files/File Operation/redo';
import { Trash, PermanentDelete, Purge } from '../Files/File Operation/trash';
import Properties from '../Properties/properties';
import Preview, { closePreviewFile } from '../Files/File Preview/preview';
import { ensureElementInViewPort } from '../Functions/viewport';
let selectedAll = true;
let pauseEnterListener = false;
/**
Expand All @@ -48,6 +49,8 @@ const pauseEnter = (): void => {
* @returns {void}
*/
const Shortcut = (): void => {
let searchingFileName = '';
let _searchListener: ReturnType<typeof setTimeout>;
const KeyUpShortcutsHandler = async (e: KeyboardEvent) => {
const selectedFile = getSelected()?.[0];
const selectedFilePath = unescape(selectedFile?.dataset?.path);
Expand Down Expand Up @@ -210,6 +213,53 @@ const Shortcut = (): void => {
}
Trash(filePaths);
}
} else if (e.keyCode >= 65 && e.keyCode <= 90) {
// ignore some keys that has its own function
if (e.ctrlKey && (e.key === 'a' || e.key === 'p' || e.key === 'f')) return;
clearInterval(_searchListener);
if (e.key.toLowerCase() === searchingFileName.at(-1)) {
const _files = [...document.querySelectorAll('.file')].filter((file: HTMLElement) => {
return file
.querySelector('#file-filename')
.innerHTML.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.startsWith(searchingFileName);
});
for (let i = 0; i < _files.length; i++) {
const _file = _files[i];
if (_file.classList.contains('selected')) {
unselectAllSelected();
Select((_files[i + 1] ?? _files[0]) as HTMLElement, false, false);
break;
}
}
} else {
searchingFileName += e.key.toLowerCase();

const _files = document.querySelectorAll('.file');
unselectAllSelected();
for (const _file of _files) {
const _fileName = _file.querySelector('#file-filename').innerHTML.toLowerCase();
console.log(_fileName.normalize('NFD').replace(/[\u0300-\u036f]/g, ''));
if (
_fileName
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.startsWith(searchingFileName)
) {
Select(_file as HTMLElement, false, false);
ensureElementInViewPort(_file as HTMLElement);
ChangeSelectedEvent();
break;
}
}
}

_searchListener = setInterval(() => {
searchingFileName = '';
clearInterval(_searchListener);
}, 750);
}
};
const KeyDownShortcutsHandler = async (e: KeyboardEvent) => {
Expand Down

0 comments on commit ef81720

Please sign in to comment.