diff --git a/packages/svelte-file-tree/package.json b/packages/svelte-file-tree/package.json index d278f73..1ec1c38 100644 --- a/packages/svelte-file-tree/package.json +++ b/packages/svelte-file-tree/package.json @@ -38,19 +38,19 @@ "svelte-signals": "^0.0.2" }, "devDependencies": { - "@sveltejs/kit": "^2.20.2", + "@sveltejs/kit": "^2.20.4", "@sveltejs/package": "^2.3.10", "@sveltejs/vite-plugin-svelte": "5.0.3", - "@types/node": "^22.13.14", - "@vitest/browser": "^3.0.9", + "@types/node": "^22.14.0", + "@vitest/browser": "^3.1.1", "jsdom": "^26.0.0", "prettier": "^3.5.3", "prettier-plugin-svelte": "^3.3.3", - "publint": "^0.3.9", - "svelte": "5.25.3", + "publint": "^0.3.10", + "svelte": "5.25.6", "svelte-check": "^4.1.5", - "vite": "^6.2.3", - "vitest": "3.0.9", + "vite": "^6.2.5", + "vitest": "3.1.1", "vitest-browser-svelte": "^0.1.0" }, "repository": { diff --git a/packages/svelte-file-tree/src/lib/components/Tree/Tree.svelte b/packages/svelte-file-tree/src/lib/components/Tree/Tree.svelte index 292f6b9..ff5f3c7 100644 --- a/packages/svelte-file-tree/src/lib/components/Tree/Tree.svelte +++ b/packages/svelte-file-tree/src/lib/components/Tree/Tree.svelte @@ -1,5 +1,6 @@ -
{#each treeState.items() as i (i.node.id)} {#if i.visible()} - {@render item({ - item: i, - select: () => selectedIds.add(i.node.id), - deselect: () => selectedIds.delete(i.node.id), - expand: () => expandedIds.add(i.node.id), - collapse: () => expandedIds.delete(i.node.id), - rename: (name) => treeState.rename(i, name), - copy: (operation) => treeState.copy(i, operation), - paste: (position) => treeState.paste(i, position), - remove: () => treeState.remove(i), - })} + {@render item({ item: i })} {/if} {/each} diff --git a/packages/svelte-file-tree/src/lib/components/Tree/TreeItemProvider.svelte b/packages/svelte-file-tree/src/lib/components/Tree/TreeItemProvider.svelte index c279bfe..f800d89 100644 --- a/packages/svelte-file-tree/src/lib/components/Tree/TreeItemProvider.svelte +++ b/packages/svelte-file-tree/src/lib/components/Tree/TreeItemProvider.svelte @@ -1,5 +1,5 @@ - - - - - diff --git a/sites/preview/src/lib/components/TreeContextMenu.svelte b/sites/preview/src/lib/components/TreeContextMenu.svelte deleted file mode 100644 index bde4787..0000000 --- a/sites/preview/src/lib/components/TreeContextMenu.svelte +++ /dev/null @@ -1,284 +0,0 @@ - - - - - menuState !== undefined, handleOpenChange}> - {@render children()} - - - - {#if menuState?.type === "item"} - {#if menuState.item().editable()} - - - Rename - - {/if} - - - - Copy - - - - - Cut - - - - - Paste - - - - - Delete - - {/if} - - {@const isFolder = menuState?.type === "item" && menuState.item().node.type === "folder"} - {#if menuState?.type === "tree" || isFolder} - - - New Folder - - - - - Upload Files - - {/if} - - - - - - - diff --git a/sites/preview/src/lib/components/TreeContextMenuTrigger.svelte b/sites/preview/src/lib/components/TreeContextMenuTrigger.svelte deleted file mode 100644 index b333473..0000000 --- a/sites/preview/src/lib/components/TreeContextMenuTrigger.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - - diff --git a/sites/preview/src/lib/components/TreeItem.svelte b/sites/preview/src/lib/components/TreeItem.svelte deleted file mode 100644 index a54449c..0000000 --- a/sites/preview/src/lib/components/TreeItem.svelte +++ /dev/null @@ -1,75 +0,0 @@ - - - diff --git a/sites/preview/src/lib/components/TreeItemToggle.svelte b/sites/preview/src/lib/components/TreeItemToggle.svelte deleted file mode 100644 index a1127f4..0000000 --- a/sites/preview/src/lib/components/TreeItemToggle.svelte +++ /dev/null @@ -1,61 +0,0 @@ - - - diff --git a/sites/preview/src/lib/components/index.ts b/sites/preview/src/lib/components/index.ts new file mode 100644 index 0000000..5b609aa --- /dev/null +++ b/sites/preview/src/lib/components/index.ts @@ -0,0 +1 @@ +export { default as Tree } from "./tree/Tree.svelte"; diff --git a/sites/preview/src/lib/components/tree/ContextMenu.svelte b/sites/preview/src/lib/components/tree/ContextMenu.svelte new file mode 100644 index 0000000..4e06605 --- /dev/null +++ b/sites/preview/src/lib/components/tree/ContextMenu.svelte @@ -0,0 +1,246 @@ + + + openArgs !== undefined, handleOpenChange}> + + {@render children()} + + + + + {#if openArgs?.type === "item"} + {@const item = openArgs.item()} + {#if item.editable()} + + + Rename + + {/if} + + onCopy(item, "copy")}> + + Copy + + + onCopy(item, "cut")}> + + Cut + + + onPaste(item)}> + + Paste + + + onRemove(item)}> + + Delete + + {/if} + + {@const isFolder = openArgs?.type === "item" && openArgs.item().node.type === "folder"} + {#if openArgs?.type === "tree" || isFolder} + + + New Folder + + + + + Upload Files + + {/if} + + + + + diff --git a/sites/preview/src/lib/components/TreeContextMenuItem.svelte b/sites/preview/src/lib/components/tree/ContextMenuItem.svelte similarity index 100% rename from sites/preview/src/lib/components/TreeContextMenuItem.svelte rename to sites/preview/src/lib/components/tree/ContextMenuItem.svelte diff --git a/sites/preview/src/lib/components/NameConflictDialog.svelte b/sites/preview/src/lib/components/tree/NameConflictDialog.svelte similarity index 72% rename from sites/preview/src/lib/components/NameConflictDialog.svelte rename to sites/preview/src/lib/components/tree/NameConflictDialog.svelte index a12d42d..78b070d 100644 --- a/sites/preview/src/lib/components/NameConflictDialog.svelte +++ b/sites/preview/src/lib/components/tree/NameConflictDialog.svelte @@ -3,35 +3,25 @@ import type { NameConflictResolution } from "svelte-file-tree"; import { fade, fly } from "svelte/transition"; - let open = $state.raw(false); - let title = $state.raw(""); - let description = $state.raw(""); - let onClose: ((resolution: NameConflictResolution) => void) | undefined; - type OpenArgs = { title: string; description: string; - onClose?: (resolution: NameConflictResolution) => void; + onClose: (resolution: NameConflictResolution) => void; }; - export function show(args: OpenArgs): void { - if (open) { + let openArgs: OpenArgs | undefined = $state.raw(); + + export function open(args: OpenArgs): void { + if (openArgs !== undefined) { throw new Error("Dialog is already open"); } - open = true; - title = args.title; - description = args.description; - onClose = args.onClose; + openArgs = args; } - function close(resolution: NameConflictResolution): void { - onClose?.(resolution); - - open = false; - title = ""; - description = ""; - onClose = undefined; + export function close(resolution: NameConflictResolution): void { + openArgs?.onClose(resolution); + openArgs = undefined; } function handleOpenChange(open: boolean): void { @@ -41,10 +31,10 @@ } - open, handleOpenChange}> + openArgs !== undefined, handleOpenChange}> - {#snippet child({ props })} + {#snippet child({ props, open })} {#if open}
{/if} @@ -55,15 +45,15 @@ forceMount class="fixed top-0 left-1/2 z-50 w-xs -translate-x-1/2 rounded-b-lg bg-neutral-100 p-4 md:w-md" > - {#snippet child({ props })} + {#snippet child({ props, open })} {#if open}
- {title} + {openArgs?.title} - {description} + {openArgs?.description}
diff --git a/sites/preview/src/lib/components/NewFolderDialog.svelte b/sites/preview/src/lib/components/tree/NewFolderDialog.svelte similarity index 77% rename from sites/preview/src/lib/components/NewFolderDialog.svelte rename to sites/preview/src/lib/components/tree/NewFolderDialog.svelte index 394b48a..1c0630c 100644 --- a/sites/preview/src/lib/components/NewFolderDialog.svelte +++ b/sites/preview/src/lib/components/tree/NewFolderDialog.svelte @@ -5,27 +5,24 @@ const nameId = $props.id(); - let open = $state.raw(false); - let name = $state.raw(""); - let onSubmit: ((name: string) => boolean) | undefined; - type OpenArgs = { - onSubmit?: (name: string) => boolean; + onSubmit: (name: string) => void; }; - export function show(args: OpenArgs): void { - if (open) { + let openArgs: OpenArgs | undefined = $state.raw(); + let name = $state.raw(""); + + export function open(args: OpenArgs): void { + if (openArgs !== undefined) { throw new Error("Dialog is already open"); } - open = true; - onSubmit = args.onSubmit; + openArgs = args; } export function close(): void { - open = false; + openArgs = undefined; name = ""; - onSubmit = undefined; } function handleOpenChange(open: boolean): void { @@ -35,23 +32,19 @@ } const handleSubmit: EventHandler = (event) => { - event.preventDefault(); - - if (onSubmit !== undefined) { - const didSubmit = onSubmit(name); - if (!didSubmit) { - return; - } + if (openArgs === undefined) { + throw new Error("Dialog is closed"); } - close(); + event.preventDefault(); + openArgs.onSubmit(name); }; - open, handleOpenChange}> + openArgs !== undefined, handleOpenChange}> - {#snippet child({ props })} + {#snippet child({ props, open })} {#if open}
{/if} @@ -62,18 +55,18 @@ forceMount class="fixed top-1/2 left-1/2 z-50 w-xs -translate-x-1/2 -translate-y-1/2 rounded-lg bg-neutral-100 p-4 md:w-md" > - {#snippet child({ props })} + {#snippet child({ props, open })} {#if open}
New Folder -
+ diff --git a/sites/preview/src/lib/components/tree/Tree.svelte b/sites/preview/src/lib/components/tree/Tree.svelte new file mode 100644 index 0000000..739e2d7 --- /dev/null +++ b/sites/preview/src/lib/components/tree/Tree.svelte @@ -0,0 +1,253 @@ + + +
+
+
Name
+
Size
+
Kind
+
+ + + node.copy()} + onResolveNameConflict={handleResolveNameConflict} + onAlreadyExistsError={handleAlreadyExistsError} + onCircularReferenceError={handleCircularReferenceError} + onfocusout={() => { + focusedItem = undefined; + }} + > + {#snippet item({ item })} + expandedIds.add(item.node.id)} + onCollapse={() => expandedIds.delete(item.node.id)} + onfocusin={() => { + focusedItem = item; + }} + /> + {/snippet} + + + +
+
+ Items: + {tree.count} +
+ +
+ Selected: + {selectedIds.size} +
+ +
+ Clipboard: + {clipboardIds.size} +
+ +
+ Paste: + {pasteDirection} +
+ +
+ Total Size: + {formatSize(tree.size)} +
+
+
+ + + + + diff --git a/sites/preview/src/lib/components/tree/TreeItem.svelte b/sites/preview/src/lib/components/tree/TreeItem.svelte new file mode 100644 index 0000000..662455f --- /dev/null +++ b/sites/preview/src/lib/components/tree/TreeItem.svelte @@ -0,0 +1,132 @@ + + + [ + "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", + item.dragged() && "opacity-50", + dropPosition() !== undefined && + "before:pointer-events-none before:absolute before:-inset-0 before:rounded-[inherit] before:border-2", + dropPosition() === "before" && "before:border-neutral-300 before:border-t-red-500", + dropPosition() === "after" && "before:border-neutral-300 before:border-b-red-500", + dropPosition() === "inside" && "before:border-red-500", + ]} + oncontextmenu={handleContextMenu} +> +
+ + +
+ {#if item.node.type === "file"} + + {:else if item.expanded()} + + {:else} + + {/if} +
+ + {#if editing} + + {:else} + {item.node.name} + {/if} +
+
{formatSize(item.node.size)}
+
{item.node.kind}
+
diff --git a/sites/preview/src/lib/tree.svelte.ts b/sites/preview/src/lib/tree.svelte.ts new file mode 100644 index 0000000..9419cb5 --- /dev/null +++ b/sites/preview/src/lib/tree.svelte.ts @@ -0,0 +1,253 @@ +import * as tree from "svelte-file-tree"; + +function getTotalSize(nodes: Array): number { + let size = 0; + for (const node of nodes) { + size += node.size; + } + return size; +} + +export class FileTree extends tree.FileTree { + readonly size = $derived(getTotalSize(this.children)); +} + +export interface FileNodeProps extends tree.FileNodeProps { + size: number; +} + +export class FileNode extends tree.FileNode { + readonly size: number; + + constructor(props: FileNodeProps) { + super(props); + this.size = props.size; + } + + readonly kind: string = $derived.by(() => { + const { name } = this; + const dotIndex = name.lastIndexOf("."); + if (dotIndex === -1) { + return "File"; + } + + const extension = name.slice(dotIndex + 1); + switch (extension) { + case "7z": { + return "7-Zip Archive"; + } + case "app": { + return "Application"; + } + case "avi": { + return "AVI Video"; + } + case "bat": { + return "Batch File"; + } + case "bmp": { + return "Bitmap Image"; + } + case "c": { + return "C File"; + } + case "cc": + case "cpp": { + return "C++ File"; + } + case "cs": { + return "C# File"; + } + case "css": { + return "CSS File"; + } + case "csv": { + return "CSV Document"; + } + case "db": { + return "Database File"; + } + case "dll": { + return "DLL File"; + } + case "doc": + case "docx": { + return "Word Document"; + } + case "exe": { + return "Executable"; + } + case "gif": { + return "GIF Image"; + } + case "go": { + return "Go File"; + } + case "gz": { + return "GZip Archive"; + } + case "html": { + return "HTML File"; + } + case "ico": { + return "Icon File"; + } + case "java": { + return "Java File"; + } + case "jpeg": + case "jpg": { + return "JPEG Image"; + } + case "js": + case "jsx": { + return "JavaScript File"; + } + case "json": { + return "JSON File"; + } + case "kt": { + return "Kotlin File"; + } + case "less": { + return "Less File"; + } + case "md": { + return "Markdown Document"; + } + case "mov": { + return "QuickTime Video"; + } + case "mp3": { + return "MP3 Audio"; + } + case "mp4": { + return "MP4 Video"; + } + case "odt": { + return "OpenDocument Text"; + } + case "ogg": { + return "OGG Audio"; + } + case "otf": + case "ttf": { + return "Font File"; + } + case "pdf": { + return "PDF Document"; + } + case "php": { + return "PHP File"; + } + case "png": { + return "PNG Image"; + } + case "ppt": + case "pptx": { + return "PowerPoint Presentation"; + } + case "py": { + return "Python File"; + } + case "rar": { + return "RAR Archive"; + } + case "rb": { + return "Ruby File"; + } + case "rtf": { + return "Rich Text Document"; + } + case "rs": { + return "Rust File"; + } + case "sass": { + return "Sass File"; + } + case "scss": { + return "SCSS File"; + } + case "sh": { + return "Shell Script"; + } + case "sql": { + return "SQL File"; + } + case "svg": { + return "SVG Image"; + } + case "svelte": { + return "Svelte Component"; + } + case "swift": { + return "Swift File"; + } + case "tar": { + return "TAR Archive"; + } + case "tiff": { + return "TIFF Image"; + } + case "ts": + case "tsx": { + return "TypeScript File"; + } + case "txt": { + return "Text Document"; + } + case "wav": { + return "WAV Audio"; + } + case "webm": { + return "WebM Video"; + } + case "webp": { + return "WebP Image"; + } + case "xls": + case "xlsx": { + return "Excel Spreadsheet"; + } + case "xml": { + return "XML File"; + } + case "yaml": + case "yml": { + return "YAML File"; + } + case "zip": { + return "ZIP Archive"; + } + default: { + return "File"; + } + } + }); + + copy(): FileNode { + return new FileNode({ + id: crypto.randomUUID(), + name: this.name, + size: this.size, + }); + } +} + +export type FolderNodeProps = tree.FolderNodeProps; + +export class FolderNode extends tree.FolderNode { + readonly size = $derived(getTotalSize(this.children)); + + readonly kind = "Folder"; + + copy(): FolderNode { + return new FolderNode({ + id: crypto.randomUUID(), + name: this.name, + children: this.children.map((child) => child.copy()), + }); + } +} + +export type FileTreeNode = FileNode | FolderNode; diff --git a/sites/preview/src/lib/utils.ts b/sites/preview/src/lib/utils.ts new file mode 100644 index 0000000..0c1116c --- /dev/null +++ b/sites/preview/src/lib/utils.ts @@ -0,0 +1,28 @@ +const sizeFormatter = new Intl.NumberFormat(undefined, { + style: "decimal", + maximumFractionDigits: 2, +}); + +export function formatSize(size: number): string { + if (size < 1000) { + return sizeFormatter.format(size) + " B"; + } + + size /= 1000; + if (size < 1000) { + return sizeFormatter.format(size) + " KB"; + } + + size /= 1000; + if (size < 1000) { + return sizeFormatter.format(size) + " MB"; + } + + size /= 1000; + if (size < 1000) { + return sizeFormatter.format(size) + " GB"; + } + + size /= 1000; + return sizeFormatter.format(size) + " TB"; +} diff --git a/sites/preview/src/routes/+page.svelte b/sites/preview/src/routes/+page.svelte index 932f9c5..045d20c 100644 --- a/sites/preview/src/routes/+page.svelte +++ b/sites/preview/src/routes/+page.svelte @@ -1,488 +1,26 @@ -
-
-
Name
-
Size
-
Kind
-
- - - - { - focusedItem = undefined; - }} - > - {#snippet item({ item, expand, collapse, copy, paste, remove })} - [ - "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", - item.dragged() && "opacity-50", - dropPosition() !== undefined && - "before:pointer-events-none before:absolute before:-inset-0 before:rounded-[inherit] before:border-2", - dropPosition() === "before" && "before:border-neutral-300 before:border-t-red-500", - dropPosition() === "after" && "before:border-neutral-300 before:border-b-red-500", - dropPosition() === "inside" && "before:border-red-500", - ]} - onCopy={copy} - onPaste={paste} - onDelete={remove} - onfocusin={() => { - focusedItem = item; - }} - > - {#snippet children({ editing })} -
- - -
- {#if item.node.type === "file"} - - {:else if item.expanded()} - - {:else} - - {/if} -
- - {#if editing()} - - {:else} - {item.node.data.name} - {/if} -
-
{formatSize(item.node.data.size)}
-
{getKind(item.node)}
- {/snippet} -
- {/snippet} -
-
-
- -
-
- Items: - {totalCount} -
- -
- Selected: - {selectedIds.size} -
- -
- Clipboard: - {clipboardIds.size} -
- -
- Paste: - {pasteDirection} -
- -
- Total Size: - {formatSize(totalSize)} -
-
-
- - + diff --git a/sites/preview/src/routes/files.json b/sites/preview/src/routes/files.json deleted file mode 100644 index 11956e0..0000000 --- a/sites/preview/src/routes/files.json +++ /dev/null @@ -1,187 +0,0 @@ -[ - { - "name": "Applications", - "children": [ - { - "name": "App Store.app", - "size": 10000000 - }, - { - "name": "Facetime.app", - "size": 15000000 - }, - { - "name": "Mail.app", - "size": 25000000 - }, - { - "name": "Messages.app", - "size": 5000000 - }, - { - "name": "Music.app", - "size": 50000000 - }, - { - "name": "Safari.app", - "size": 14000000 - } - ] - }, - { - "name": "Developer", - "children": [ - { - "name": "svelte-file-tree", - "children": [ - { - "name": "src", - "children": [ - { - "name": "components", - "children": [ - { - "name": "Tree.svelte", - "size": 3000 - }, - { - "name": "TreeItem.svelte", - "size": 1100 - }, - { - "name": "TreeItemInput.svelte", - "size": 2000 - } - ] - }, - { - "name": "index.ts", - "size": 500 - }, - { - "name": "tree.svelte.ts", - "size": 1000 - } - ] - }, - { - "name": "package.json", - "size": 2000 - }, - { - "name": "README.md", - "size": 1000 - } - ] - }, - { - "name": "svelte-material-ripple", - "children": [ - { - "name": "src", - "children": [ - { - "name": "index.ts", - "size": 200 - }, - { - "name": "Ripple.svelte", - "size": 10000 - } - ] - }, - { - "name": "package.json", - "size": 3000 - }, - { - "name": "README.md", - "size": 1000 - } - ] - } - ] - }, - { - "name": "Documents", - "children": [ - { - "name": "Project Planning", - "children": [ - { - "name": "q1-goals.xlsx", - "size": 22000000 - }, - { - "name": "timeline.pdf", - "size": 1500000 - } - ] - }, - { - "name": "meeting-notes.docx", - "size": 10000 - }, - { - "name": "resume.pdf", - "size": 1200000 - } - ] - }, - { - "name": "Downloads", - "children": [ - { - "name": "conference-slides.pptx", - "size": 5000000 - }, - { - "name": "typescript-cheatsheet.pdf", - "size": 50000 - } - ] - }, - { - "name": "Movies", - "children": [ - { - "name": "Finding Nemo.mp4", - "size": 1000000000 - }, - { - "name": "Inside Out.mp4", - "size": 1000000000 - }, - { - "name": "Up.mp4", - "size": 1000000000 - } - ] - }, - { - "name": "Pictures", - "children": [ - { - "name": "Screenshots", - "children": [ - { - "name": "bug-report.png", - "size": 300000 - }, - { - "name": "component-diagram.png", - "size": 400000 - }, - { - "name": "design-mockup.png", - "size": 350000 - } - ] - }, - { - "name": "profile-photo.jpg", - "size": 200000 - } - ] - } -] diff --git a/sites/preview/src/routes/files.ts b/sites/preview/src/routes/files.ts new file mode 100644 index 0000000..bd1ebe8 --- /dev/null +++ b/sites/preview/src/routes/files.ts @@ -0,0 +1,197 @@ +export type FileData = + | { + name: string; + children: Array; + } + | { + name: string; + size: number; + }; + +export const files: Array = [ + { + name: "Applications", + children: [ + { + name: "App Store.app", + size: 10000000, + }, + { + name: "Facetime.app", + size: 15000000, + }, + { + name: "Mail.app", + size: 25000000, + }, + { + name: "Messages.app", + size: 5000000, + }, + { + name: "Music.app", + size: 50000000, + }, + { + name: "Safari.app", + size: 14000000, + }, + ], + }, + { + name: "Developer", + children: [ + { + name: "svelte-file-tree", + children: [ + { + name: "src", + children: [ + { + name: "components", + children: [ + { + name: "Tree.svelte", + size: 3000, + }, + { + name: "TreeItem.svelte", + size: 1100, + }, + { + name: "TreeItemInput.svelte", + size: 2000, + }, + ], + }, + { + name: "index.ts", + size: 500, + }, + { + name: "tree.svelte.ts", + size: 1000, + }, + ], + }, + { + name: "package.json", + size: 2000, + }, + { + name: "README.md", + size: 1000, + }, + ], + }, + { + name: "svelte-material-ripple", + children: [ + { + name: "src", + children: [ + { + name: "index.ts", + size: 200, + }, + { + name: "Ripple.svelte", + size: 10000, + }, + ], + }, + { + name: "package.json", + size: 3000, + }, + { + name: "README.md", + size: 1000, + }, + ], + }, + ], + }, + { + name: "Documents", + children: [ + { + name: "Project Planning", + children: [ + { + name: "q1-goals.xlsx", + size: 22000000, + }, + { + name: "timeline.pdf", + size: 1500000, + }, + ], + }, + { + name: "meeting-notes.docx", + size: 10000, + }, + { + name: "resume.pdf", + size: 1200000, + }, + ], + }, + { + name: "Downloads", + children: [ + { + name: "conference-slides.pptx", + size: 5000000, + }, + { + name: "typescript-cheatsheet.pdf", + size: 50000, + }, + ], + }, + { + name: "Movies", + children: [ + { + name: "Finding Nemo.mp4", + size: 1000000000, + }, + { + name: "Inside Out.mp4", + size: 1000000000, + }, + { + name: "Up.mp4", + size: 1000000000, + }, + ], + }, + { + name: "Pictures", + children: [ + { + name: "Screenshots", + children: [ + { + name: "bug-report.png", + size: 300000, + }, + { + name: "component-diagram.png", + size: 400000, + }, + { + name: "design-mockup.png", + size: 350000, + }, + ], + }, + { + name: "profile-photo.jpg", + size: 200000, + }, + ], + }, +];