|
2 | 2 | import { Tree, type FolderNode } from "svelte-file-tree";
|
3 | 3 | import { flip } from "svelte/animate";
|
4 | 4 | import { SvelteSet } from "svelte/reactivity";
|
| 5 | + import { isControlOrMeta } from "$lib/helpers.js"; |
5 | 6 | import TreeItem from "./TreeItem.svelte";
|
6 | 7 | import type { TreeProps } from "./types.js";
|
7 | 8 |
|
8 | 9 | const { root }: TreeProps = $props();
|
9 | 10 |
|
10 | 11 | const expandedIds = new SvelteSet<string>();
|
11 | 12 | let dropDestination: FolderNode | undefined = $state.raw();
|
| 13 | +
|
| 14 | + let tree: Tree | null = $state.raw(null); |
12 | 15 | </script>
|
13 | 16 |
|
14 |
| -<Tree |
15 |
| - {root} |
16 |
| - {expandedIds} |
17 |
| - class={[ |
18 |
| - "min-h-svh p-8", |
19 |
| - { |
20 |
| - "before:pointer-events-none before:absolute before:inset-2 before:border-2 before:border-red-500": |
21 |
| - dropDestination === root, |
22 |
| - }, |
23 |
| - ]} |
24 |
| - onChildrenChange={(args) => { |
25 |
| - if (args.operation === "insert") { |
26 |
| - args.children.sort((a, b) => a.name.localeCompare(b.name)); |
27 |
| - } |
28 |
| - }} |
29 |
| - onDropDestinationChange={(args) => { |
30 |
| - dropDestination = args.dropDestination; |
31 |
| - }} |
32 |
| -> |
33 |
| - {#snippet children({ items })} |
34 |
| - {#each items.filter((item) => item.visible) as item (item.node.id)} |
35 |
| - <div animate:flip={{ duration: 300 }}> |
36 |
| - <TreeItem |
37 |
| - {item} |
38 |
| - {dropDestination} |
39 |
| - onExpand={() => { |
40 |
| - expandedIds.add(item.node.id); |
41 |
| - }} |
42 |
| - onCollapse={() => { |
43 |
| - expandedIds.delete(item.node.id); |
44 |
| - }} |
45 |
| - /> |
46 |
| - </div> |
47 |
| - {/each} |
48 |
| - {/snippet} |
49 |
| -</Tree> |
| 17 | +<div class="flex min-h-svh p-2"> |
| 18 | + <Tree |
| 19 | + {root} |
| 20 | + {expandedIds} |
| 21 | + bind:this={tree} |
| 22 | + tabindex={0} |
| 23 | + class={[ |
| 24 | + "relative grow p-6 focus-visible:outline-2 focus-visible:outline-current", |
| 25 | + { |
| 26 | + "before:pointer-events-none before:absolute before:inset-0 before:border-2 before:border-red-500": |
| 27 | + dropDestination === root, |
| 28 | + }, |
| 29 | + ]} |
| 30 | + onChildrenChange={(args) => { |
| 31 | + if (args.operation === "insert") { |
| 32 | + args.children.sort((a, b) => a.name.localeCompare(b.name)); |
| 33 | + } |
| 34 | + }} |
| 35 | + onDropDestinationChange={(args) => { |
| 36 | + dropDestination = args.dropDestination; |
| 37 | + }} |
| 38 | + onkeydown={(event) => { |
| 39 | + if (event.key === "v" && isControlOrMeta(event)) { |
| 40 | + event.preventDefault(); |
| 41 | + tree!.paste(root); |
| 42 | + } |
| 43 | + }} |
| 44 | + > |
| 45 | + {#snippet children({ items })} |
| 46 | + {#each items.filter((item) => item.visible) as item (item.node.id)} |
| 47 | + <div animate:flip={{ duration: 300 }}> |
| 48 | + <TreeItem |
| 49 | + {item} |
| 50 | + {dropDestination} |
| 51 | + onExpand={() => { |
| 52 | + expandedIds.add(item.node.id); |
| 53 | + }} |
| 54 | + onCollapse={() => { |
| 55 | + expandedIds.delete(item.node.id); |
| 56 | + }} |
| 57 | + /> |
| 58 | + </div> |
| 59 | + {/each} |
| 60 | + {/snippet} |
| 61 | + </Tree> |
| 62 | +</div> |
0 commit comments