Skip to content

Commit d01930d

Browse files
authored
Merge pull request #56 from abdel-17/status-bar
preview: status bar
2 parents efba201 + 374dbc2 commit d01930d

File tree

2 files changed

+119
-25
lines changed

2 files changed

+119
-25
lines changed

sites/preview/src/lib/components/TreeContextMenu.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@
224224
<ContextMenu.Portal>
225225
<ContextMenu.Content
226226
{trapFocus}
227-
class="w-[200px] rounded-xl border border-gray-300 bg-gray-50 p-2 shadow focus-visible:outline-none"
227+
class="z-50 w-[200px] rounded-xl border border-gray-300 bg-gray-50 p-2 shadow focus-visible:outline-none"
228228
onCloseAutoFocus={handleContentCloseAutoFocus}
229229
>
230230
{#if menuState?.type === "item"}

sites/preview/src/routes/+page.svelte

Lines changed: 118 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
FolderNode,
1212
TreeItemInput,
1313
type FileTreeNode,
14+
type PasteOperation,
15+
type TreeItemState,
1416
} from "svelte-file-tree";
17+
import { SvelteSet } from "svelte/reactivity";
1518
import files from "./files.json" with { type: "json" };
1619
1720
type File = {
@@ -49,6 +52,50 @@
4952
}),
5053
);
5154
55+
const selectedIds = new SvelteSet<string>();
56+
const clipboardIds = new SvelteSet<string>();
57+
let pasteOperation: PasteOperation | undefined = $state.raw();
58+
let focusedItem: TreeItemState<NodeData> | undefined = $state.raw();
59+
60+
function getTotalCount(nodes: Array<FileTreeNode<NodeData>>): number {
61+
let count = 0;
62+
for (const node of nodes) {
63+
count++;
64+
65+
if (node.type === "folder") {
66+
count += getTotalCount(node.children);
67+
}
68+
}
69+
return count;
70+
}
71+
72+
function getTotalSize(nodes: Array<FileTreeNode<NodeData>>): number {
73+
let size = 0;
74+
for (const node of nodes) {
75+
size += node.data.size ?? 0;
76+
77+
if (node.type === "folder") {
78+
size += getTotalSize(node.children);
79+
}
80+
}
81+
return size;
82+
}
83+
84+
const totalCount = $derived(getTotalCount(tree.children));
85+
const totalSize = $derived(getTotalSize(tree.children));
86+
87+
const pasteDirection = $derived.by((): string | undefined => {
88+
if (pasteOperation === undefined || focusedItem === undefined) {
89+
return;
90+
}
91+
92+
if (focusedItem.node.type === "folder" && focusedItem.expanded()) {
93+
return "Inside";
94+
}
95+
96+
return "After";
97+
});
98+
5299
function getFileKind(name: string): string | undefined {
53100
const dotIndex = name.lastIndexOf(".");
54101
if (dotIndex === -1) {
@@ -267,30 +314,30 @@
267314
return "-";
268315
}
269316
270-
if (size < 1_000) {
317+
if (size < 1000) {
271318
return sizeFormatter.format(size) + " B";
272319
}
273320
274-
size /= 1_000;
275-
if (size < 1_000) {
321+
size /= 1000;
322+
if (size < 1000) {
276323
return sizeFormatter.format(size) + " KB";
277324
}
278325
279-
size /= 1_000;
280-
if (size < 1_000) {
326+
size /= 1000;
327+
if (size < 1000) {
281328
return sizeFormatter.format(size) + " MB";
282329
}
283330
284331
size /= 1000;
285-
if (size < 1_000) {
332+
if (size < 1000) {
286333
return sizeFormatter.format(size) + " GB";
287334
}
288335
289336
size /= 1000;
290337
return sizeFormatter.format(size) + " TB";
291338
}
292339
293-
function onUploadFiles({
340+
function handleUploadFiles({
294341
target,
295342
files,
296343
}: {
@@ -310,7 +357,7 @@
310357
}
311358
}
312359
313-
function onCreateNewFolder({
360+
function handleCreateNewFolder({
314361
target,
315362
name,
316363
}: {
@@ -326,25 +373,35 @@
326373
}
327374
</script>
328375

329-
<main class="flex min-h-svh flex-col p-8">
330-
<TreeContextMenu {tree} {onUploadFiles} {onCreateNewFolder}>
331-
<TreeContextMenuTrigger class="grow rounded border border-gray-400 p-5">
332-
<div
333-
class="grid grid-cols-(--grid-cols) gap-x-(--grid-gap) px-(--grid-inline-padding) text-sm font-semibold"
334-
>
335-
<div>Name</div>
336-
<div>Size</div>
337-
<div>Kind</div>
338-
</div>
376+
<main class="flex h-svh flex-col">
377+
<div
378+
class="grid grid-cols-(--grid-cols) gap-x-(--grid-gap) border-b border-gray-300 px-[calc(var(--tree-inline-padding)+var(--grid-inline-padding))] py-3 text-sm font-semibold"
379+
>
380+
<div>Name</div>
381+
<div>Size</div>
382+
<div>Kind</div>
383+
</div>
339384

340-
<Tree {tree} isItemEditable class="mt-2">
385+
<TreeContextMenu
386+
{tree}
387+
onUploadFiles={handleUploadFiles}
388+
onCreateNewFolder={handleCreateNewFolder}
389+
>
390+
<TreeContextMenuTrigger class="grow overflow-y-auto rounded px-(--tree-inline-padding) py-2">
391+
<Tree
392+
{tree}
393+
{selectedIds}
394+
{clipboardIds}
395+
bind:pasteOperation
396+
isItemEditable
397+
onfocusout={() => {
398+
focusedItem = undefined;
399+
}}
400+
>
341401
{#snippet item({ item, expand, collapse, copy, paste, remove })}
342402
<TreeItem
343403
{item}
344404
draggable
345-
onCopy={copy}
346-
onPaste={paste}
347-
onDelete={remove}
348405
class={({ dropPosition }) => [
349406
"relative grid grid-cols-(--grid-cols) gap-x-(--grid-gap) rounded-md p-(--grid-inline-padding) hover:bg-neutral-200 focus:outline-2 focus:-outline-offset-2 focus:outline-current active:bg-neutral-300 aria-selected:bg-blue-200 aria-selected:text-blue-900 aria-selected:active:bg-blue-300 aria-selected:has-[+[aria-selected='true']]:rounded-b-none aria-selected:[&+[aria-selected='true']]:rounded-t-none",
350407
item.dragged() && "opacity-50",
@@ -354,12 +411,21 @@
354411
dropPosition() === "after" && "before:border-neutral-300 before:border-b-red-500",
355412
dropPosition() === "inside" && "before:border-red-500",
356413
]}
414+
onCopy={copy}
415+
onPaste={paste}
416+
onDelete={remove}
417+
onfocusin={() => {
418+
focusedItem = item;
419+
}}
357420
>
358421
{#snippet children({ editing })}
359-
<div class="flex items-center" style="padding-inline-start: {item.depth * 24}px">
422+
<div
423+
class="flex items-center"
424+
style="padding-inline-start: calc(var(--spacing) * {item.depth * 6})"
425+
>
360426
<TreeItemToggle {item} onExpand={expand} onCollapse={collapse} />
361427

362-
<div class="ms-1 me-2">
428+
<div class="ps-1 pe-2">
363429
{#if item.node.type === "file"}
364430
<FileIcon role="presentation" />
365431
{:else if item.expanded()}
@@ -383,12 +449,40 @@
383449
</Tree>
384450
</TreeContextMenuTrigger>
385451
</TreeContextMenu>
452+
453+
<div class="grid shrink-0 grid-cols-5 place-items-center bg-gray-200 p-2 text-sm">
454+
<div>
455+
<span class="font-medium text-gray-700">Items:</span>
456+
<span class="font-semibold text-gray-900">{totalCount}</span>
457+
</div>
458+
459+
<div>
460+
<span class="font-medium text-gray-700">Selected:</span>
461+
<span class="font-semibold text-gray-900">{selectedIds.size}</span>
462+
</div>
463+
464+
<div>
465+
<span class="font-medium text-gray-700">Clipboard:</span>
466+
<span class="font-semibold text-gray-900">{clipboardIds.size}</span>
467+
</div>
468+
469+
<div>
470+
<span class="font-medium text-gray-700">Paste:</span>
471+
<span class="font-semibold text-gray-900">{pasteDirection}</span>
472+
</div>
473+
474+
<div>
475+
<span class="font-medium text-gray-700">Total Size:</span>
476+
<span class="font-semibold text-gray-900">{formatSize(totalSize)}</span>
477+
</div>
478+
</div>
386479
</main>
387480

388481
<style>
389482
:root {
390483
--grid-cols: 5fr 1fr 2fr;
391484
--grid-gap: calc(var(--spacing) * 4);
392485
--grid-inline-padding: calc(var(--spacing) * 3);
486+
--tree-inline-padding: calc(var(--spacing) * 6);
393487
}
394488
</style>

0 commit comments

Comments
 (0)