implement sorting for file explorer

This commit is contained in:
Bao Nguyen
2023-02-18 08:37:47 +07:00
parent 328398bff8
commit e7e6a77f6f
8 changed files with 147 additions and 31 deletions

22
package-lock.json generated
View File

@@ -18,7 +18,9 @@
"build-url": "^6.0.1",
"docx": "^7.7.0",
"dragselect": "^2.5.5",
"fast-sort": "^3.2.1",
"file-saver": "^2.0.5",
"hash-sum": "^2.0.0",
"idb-keyval": "^6.2.0",
"is-valid-http-url": "^1.0.3",
"jsdom": "^20.0.3",
@@ -2225,6 +2227,11 @@
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
},
"node_modules/fast-sort": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/fast-sort/-/fast-sort-3.2.1.tgz",
"integrity": "sha512-ECGwVH57yCv3C8v+4BQkQf65bNR4nFetxfGRRLYmzFEAK3oZBr7zCakBjOY/AptmmoU3ffMPJLLN1rdKdMHoUA=="
},
"node_modules/fastq": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
@@ -2450,6 +2457,11 @@
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
},
"node_modules/hash-sum": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
},
"node_modules/html-encoding-sniffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
@@ -5952,6 +5964,11 @@
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
},
"fast-sort": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/fast-sort/-/fast-sort-3.2.1.tgz",
"integrity": "sha512-ECGwVH57yCv3C8v+4BQkQf65bNR4nFetxfGRRLYmzFEAK3oZBr7zCakBjOY/AptmmoU3ffMPJLLN1rdKdMHoUA=="
},
"fastq": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
@@ -6116,6 +6133,11 @@
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
},
"hash-sum": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
"integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
},
"html-encoding-sniffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",

View File

@@ -32,7 +32,9 @@
"build-url": "^6.0.1",
"docx": "^7.7.0",
"dragselect": "^2.5.5",
"fast-sort": "^3.2.1",
"file-saver": "^2.0.5",
"hash-sum": "^2.0.0",
"idb-keyval": "^6.2.0",
"is-valid-http-url": "^1.0.3",
"jsdom": "^20.0.3",

View File

@@ -28,6 +28,7 @@
};
async function load_preview(){
console.log(preview_url)
if(preview_url != null) return;
if(fs_id == null) return;
let url = await fs.get_url(fs_id);

View File

@@ -158,7 +158,7 @@
</div>
<div class="w-full bg-slate-50 grow border overflow-auto border-blue-300" class:hidden={id == null}
on:click|self={clear_selection}>
{#each items as item}
{#each items as item (item.id)}
<div fs-id="{item.id}" class="w-[100px] overflow-hidden m-2 inline-flex flex-row items-center font-MSSS relative
{is_desired(item) ? '' : 'opacity-50'}"
on:dblclick={() => open(item.id)}

View File

@@ -128,7 +128,7 @@
</div>
<div class="absolute top-7 left-1 right-1 h-[360px] overflow-auto bg-slate-50 border border-blue-300" class:hidden={id == null}>
{#each items as item}
{#each items as item (item.id)}
<div fs-id="{item.id}" class="w-[100px] overflow-hidden m-2 inline-flex flex-row items-center font-MSSS relative
{is_desired(item) ? '' : 'opacity-50'}"
on:dblclick={() => open(item.id)}>

View File

@@ -231,7 +231,7 @@
<div class="top-0 left-0 bottom-0 absolute flex flex-col flex-wrap"
class:hidden={id == null}>
{#each items as item, index}
{#each items as item, index (item.id)}
<div fs-id="{item.id}" class="relative fs-item w-[150px] flex-shrink-0 flex-grow-0 overflow-hidden m-2 inline-flex flex-col items-center font-MSSS"
on:dblclick={() => open(item.id)} on:contextmenu={(e) => on_rightclick(e, item)}

View File

@@ -0,0 +1,58 @@
import { sort } from 'fast-sort';
import { SortOptions, SortOrders } from '../../../../lib/system';
let cache = {};
onmessage = async ({data}) => {
let {type, hash, id, items, sort_option, sort_order} = data;
if(type != 'sort') return;
if(sort_option == SortOptions.NONE){
postMessage({id, sorted_items: items, type: 'sorted'});
return;
}
if(cache[hash] != null){
postMessage({id, sorted_items: cache[hash], type: 'sorted'});
return;
}
console.log('sorting', hash);
for(let item of items){
if(item.type == 'folder'){
item.size = 0;
}
}
let order_key = sort_order == SortOrders.ASCENDING ? 'asc' : 'desc';
let option_key = 'name';
switch (sort_option) {
case SortOptions.NAME:
option_key = 'name';
break;
case SortOptions.SIZE:
option_key = 'size';
break;
case SortOptions.DATE_CREATED:
option_key = 'date_created';
break;
case SortOptions.DATE_MODIFIED:
option_key = 'date_modified';
break;
default:
break;
}
let predicate = {};
predicate[order_key] = elm => {
if(option_key == 'name'){
return elm[option_key].toLowerCase();
} else {
return elm[option_key];
}
}
let sorted_items = sort(items).by([predicate]);
postMessage({id, sorted_items, type: 'sorted'})
}

View File

@@ -14,6 +14,7 @@
let dispatch = createEventDispatcher();
import DragSelect from 'dragselect';
import Previewable from '../../../../lib/components/xp/Previewable.svelte';
import hash_sum from 'hash-sum';
export let self;
export let my_computer_instance;
@@ -27,6 +28,34 @@
.filter(el => el != null)
.filter(el => !hidden_items.includes(el.id));
$: sorted_items = id ? null : null;//reset sorted_items every time id changes
let worker = new Worker(new URL('./sort.js', import.meta.url), {type: 'module'});
worker.onmessage = ({data}) => {
if(data.type == 'sorted' && data.id == id){
console.log('update sorted_items', id);
sorted_items = data.sorted_items;
}
}
let last_sort_tx_hash;
$: {
if(id){
let hash_object = {
id,
items,
sort_option: $hardDrive[id].sort_option,
sort_order: $hardDrive[id].sort_order
};
let hash = hash_sum(hash_object);
console.log({hash})
if(hash != last_sort_tx_hash){
last_sort_tx_hash = hash;
worker.postMessage({type: 'sort', hash, ...hash_object});
}
}
}
$: is_focus = $zIndex == my_computer_instance?.window.z_index;
let computer = my_computer.map(el => $hardDrive[el]);
@@ -257,34 +286,38 @@
on:drop={on_drop} on:dragover={on_drop_over} bind:this={node_ref}>
<div class="w-full min-h-full" class:hidden={id == null}
on:contextmenu|self={show_void_menu}>
{#each items as item}
<div fs-id="{item.id}" class="fs-item w-[150px] overflow-hidden m-2 inline-flex flex-row items-center font-MSSS relative
{$clipboard.includes(item.id) && $clipboard_op == 'cut' ? 'opacity-70' : ''}"
on:dblclick={() => open(item.id)} on:contextmenu={(e) => on_rightclick(e, item)}>
{#if previewable_exts.includes(item.ext)}
<Previewable default_icon={file_icon(item)} fs_id={item.id}></Previewable>
{:else}
<div class="w-[50px] h-[50px] shrink-0 bg-contain bg-no-repeat bg-center
{item.type == 'folder' ? 'bg-[url(/images/xp/icons/FolderClosed.png)]' : 'bg-[url(/images/xp/icons/Default.png)]'} "
style:background-image="{file_icon(item)}">
</div>
{/if}
<p class="px-1 mx-0.5 text-[11px] break-words line-clamp-2 text-ellipsis leading-tight
{$selectingItems?.includes(item.id) && is_focus ? 'bg-blue-600 text-slate-50' : ''}">
{item.name}
</p>
{#if $selectingItems.includes(item.id) && renaming}
<textarea
autofocus
on:keydown={e => e.key == 'Enter' && end_renaming(e, item)}
on:blur={(e) => end_renaming(e, item)}
class="absolute max-h-[40px] right-0 top-2 left-[50px] overflow-hidden
outline-none border border-slate-900 text-[11px] font-MSSS z-50 resize-none"
>{item.name}</textarea>
{/if}
</div>
{/each}
{#if sorted_items}
{#each sorted_items as item (item.id)}
<div fs-id="{item.id}" class="fs-item w-[150px] overflow-hidden m-2 inline-flex flex-row items-center font-MSSS relative
{$clipboard.includes(item.id) && $clipboard_op == 'cut' ? 'opacity-70' : ''}"
on:dblclick={() => open(item.id)} on:contextmenu={(e) => on_rightclick(e, item)}>
{#if previewable_exts.includes(item.ext)}
<Previewable default_icon={file_icon(item)} fs_id={item.id}></Previewable>
{:else}
<div class="w-[50px] h-[50px] shrink-0 bg-contain bg-no-repeat bg-center
{item.type == 'folder' ? 'bg-[url(/images/xp/icons/FolderClosed.png)]' : 'bg-[url(/images/xp/icons/Default.png)]'} "
style:background-image="{file_icon(item)}">
</div>
{/if}
<p class="px-1 mx-0.5 text-[11px] break-words line-clamp-2 text-ellipsis leading-tight
{$selectingItems?.includes(item.id) && is_focus ? 'bg-blue-600 text-slate-50' : ''}">
{item.name}
</p>
{#if $selectingItems.includes(item.id) && renaming}
<textarea
autofocus
on:keydown={e => e.key == 'Enter' && end_renaming(e, item)}
on:blur={(e) => end_renaming(e, item)}
class="absolute max-h-[40px] right-0 top-2 left-[50px] overflow-hidden
outline-none border border-slate-900 text-[11px] font-MSSS z-50 resize-none"
>{item.name}</textarea>
{/if}
</div>
{/each}
{:else}
<p class="text-center text-sm font-Trebuchet my-2 text-slate-500">working on it...</p>
{/if}
</div>