147 lines
6.5 KiB
Svelte
147 lines
6.5 KiB
Svelte
<script context="module" lang="ts">
|
|
import {writable} from 'svelte/store';
|
|
const show_preview = writable<boolean>(false);
|
|
</script>
|
|
<script lang="ts">
|
|
import {Checkbox, Dropdown, DropdownItem, Spinner, Table, TableBody, TableBodyCell, TableBodyRow, TableHead, TableHeadCell, Tooltip} from 'flowbite-svelte';
|
|
import {Folder, FolderParent, DocumentBlank, CaretLeft} from '../icons';
|
|
import {filesize} from 'filesize';
|
|
import {api, changeStateFunction, download, StateE, token, rpc} from '../store';
|
|
import LinkButton from './LinkButton.svelte';
|
|
import DeleteModal from './DeleteModal.svelte';
|
|
|
|
export let node: api.Node;
|
|
|
|
let selected: number[] = [];
|
|
let nodes: api.Node[], dirs: api.Node[], files: api.Node[], previews: {[key: number]: string|null} = {};
|
|
let total_size: number;
|
|
$: { nodes = node.children!; selectNone(); }
|
|
$: dirs = nodes.filter(v => !v.file).sort((a, b) => a.name.localeCompare(b.name));
|
|
$: files = nodes.filter(v => v.file).sort((a, b) => a.name.localeCompare(b.name));
|
|
$: total_size = files.map(v => v.size).reduce<number>((a, b) => (a + b!), 0);
|
|
$: {
|
|
if ($show_preview) {
|
|
for (const file of files) {
|
|
if (!file.preview) continue;
|
|
getPreview(file.id);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function getPreview(node: number) {
|
|
const resp = await rpc.FS_download_preview($token ?? '', node);
|
|
if (resp.o == null)
|
|
return;
|
|
previews[node] = 'data:image/png;base64,' + resp.o;
|
|
previews = previews;
|
|
}
|
|
|
|
let ctx_node: api.Node;
|
|
let ctx_hidden = true;
|
|
let ctx_x = 0, ctx_y = 0;
|
|
let ctx_style: string;
|
|
$: ctx_style = `top: ${ctx_y}px; left: ${ctx_x}px; position: fixed;`;
|
|
|
|
function onCtxMenu(node: api.Node, e: MouseEvent) {
|
|
e.preventDefault();
|
|
if (!ctx_hidden)
|
|
return ctx_hidden = true;
|
|
ctx_x = e.pageX;
|
|
ctx_y = e.pageY;
|
|
ctx_node = node;
|
|
ctx_hidden = false;
|
|
}
|
|
|
|
const selectAll = () => selected = nodes.map(v => v.id);
|
|
const selectFolders = () => selected = dirs.map(v => v.id);
|
|
const selectFiles = () => selected = files.map(v => v.id);
|
|
const selectNone = () => selected = [];
|
|
const downloadSelected = () => download($token ?? '', nodes.filter(v => selected.includes(v.id)));
|
|
const deleteSelected = () => del(selected);
|
|
|
|
|
|
const onCtxDownload = () => download($token ?? '', [ctx_node]);
|
|
|
|
let del: (nodes: number[]) => Promise<void>;
|
|
const onCtxDelete = () => del([ctx_node.id]);
|
|
|
|
const onShowPreview = (e: Event) => { show_preview.set((e.target as HTMLInputElement).checked); }
|
|
</script>
|
|
|
|
<svelte:body on:click={() => (ctx_hidden = true)} />
|
|
|
|
<DeleteModal bind:del={del} />
|
|
|
|
<Table hoverable>
|
|
<TableHead theadClass="text-xs">
|
|
<TableHeadCell class="p-2 pl-4 w-0 h-0">
|
|
<CaretLeft id="dropdown-button" />
|
|
</TableHeadCell>
|
|
<TableHeadCell class="p-2 w-0"><Checkbox checked={$show_preview} on:change={onShowPreview} /><Tooltip>Show image previews</Tooltip></TableHeadCell>
|
|
<TableHeadCell>Name</TableHeadCell>
|
|
<TableHeadCell>Size</TableHeadCell>
|
|
</TableHead>
|
|
<TableBody>
|
|
{#if node.parent !== null}
|
|
<TableBodyRow>
|
|
<TableBodyCell class="!p-4"></TableBodyCell>
|
|
<TableBodyCell class="px-2 w-0"><FolderParent /></TableBodyCell>
|
|
<TableBodyCell class="pl-0"><LinkButton on:click={changeStateFunction(StateE.VIEW, node.parent ?? 0)}>..</LinkButton></TableBodyCell>
|
|
<TableBodyCell></TableBodyCell>
|
|
</TableBodyRow>
|
|
{/if}
|
|
{#each dirs as node}
|
|
<TableBodyRow on:contextmenu={onCtxMenu.bind(null, node)}>
|
|
<TableBodyCell class="p-2 pl-4 w-0 h-0"><Checkbox bind:group={selected} value={node.id}/></TableBodyCell>
|
|
<TableBodyCell class="px-2 w-0"><Folder /></TableBodyCell>
|
|
<TableBodyCell class="pl-0"><LinkButton on:click={changeStateFunction(StateE.VIEW, node.id)}>{node.name}</LinkButton></TableBodyCell>
|
|
<TableBodyCell></TableBodyCell>
|
|
</TableBodyRow>
|
|
{/each}
|
|
{#each files as node}
|
|
<TableBodyRow on:contextmenu={onCtxMenu.bind(null, node)}>
|
|
<TableBodyCell class="p-2 pl-4 w-0 h-0"><Checkbox bind:group={selected} value={node.id}/></TableBodyCell>
|
|
<TableBodyCell class="px-2 min-w-0">
|
|
{#if $show_preview && node.preview}
|
|
{#if previews[node.id] !== undefined}
|
|
<img class="w-screen max-w-xs" alt="preview" src={previews[node.id]} />
|
|
{:else}
|
|
<Spinner size="4"/>
|
|
{/if}
|
|
{:else}
|
|
<DocumentBlank />
|
|
{/if}
|
|
</TableBodyCell>
|
|
<TableBodyCell class="pl-0"><LinkButton on:click={changeStateFunction(StateE.VIEW, node.id)}>{node.name}</LinkButton></TableBodyCell>
|
|
<TableBodyCell>{filesize(node.size ?? 0, {base: 2, standard: 'jedec'})}</TableBodyCell>
|
|
</TableBodyRow>
|
|
{/each}
|
|
</TableBody>
|
|
<tfoot class="text-gray-700 bg-gray-50">
|
|
<tr>
|
|
<td class="px-6 py-3" colspan="3">
|
|
{#if selected.length > 0}
|
|
<LinkButton on:click={downloadSelected}>Download</LinkButton>
|
|
<LinkButton color="red" on:click={deleteSelected}>Delete</LinkButton>
|
|
{/if}
|
|
</td>
|
|
<td class="px-6 py-3">{filesize(total_size, {base: 2, standard: 'jedec'})}</td>
|
|
</tr>
|
|
</tfoot>
|
|
</Table>
|
|
|
|
<Dropdown triggeredBy="#dropdown-button" trigger="hover" placement="left">
|
|
<DropdownItem on:click={selectAll}>Select all</DropdownItem>
|
|
<DropdownItem on:click={selectFolders}>Select folders</DropdownItem>
|
|
<DropdownItem on:click={selectFiles}>Select files</DropdownItem>
|
|
<DropdownItem on:click={selectNone}>Select none</DropdownItem>
|
|
</Dropdown>
|
|
|
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
<div style={ctx_style} hidden={ctx_hidden} class="z-50 shadow-md rounded-lg border-gray-100 bg-white" on:contextmenu={() => (ctx_hidden = true)}>
|
|
<ul class="py-1">
|
|
<li><button class="font-medium py-2 px-4 text-sm hover:bg-gray-100 w-full text-left" on:click={onCtxDownload}>Download</button></li>
|
|
<li><button class="font-medium py-2 px-4 text-sm hover:bg-gray-100 text-red-400 w-full text-left" on:click={onCtxDelete}>Delete</button></li>
|
|
</ul>
|
|
</div>
|