mirror of
https://github.com/ducbao414/win32.run.git
synced 2025-12-13 07:42:47 +09:00
implement sorting for file explorer
This commit is contained in:
22
package-lock.json
generated
22
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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)}>
|
||||
|
||||
@@ -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)}
|
||||
|
||||
58
src/routes/xp/programs/my_computer/sort.js
Normal file
58
src/routes/xp/programs/my_computer/sort.js
Normal 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'})
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user