Skip to content

Commit 50aa725

Browse files
authored
Merge pull request #72 from abdel-17/pass-copies
feat: pass copies to `onCopy`
2 parents 9f51752 + f929933 commit 50aa725

File tree

5 files changed

+67
-52
lines changed

5 files changed

+67
-52
lines changed

packages/svelte-file-tree/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# svelte-file-tree
22

3+
## 0.8.0
4+
5+
### Minor Changes
6+
7+
- feat: pass `copies` to `onCopy` and `canCopy` callbacks.
8+
- feat: add prop `TreeProps.defaultClipboardIds`.
9+
- feat: add prop `TreeProps.clipboardIds`.
10+
- feat: add bindable prop `TreeProps.pasteOperation`.
11+
- breaking: remove prop `TreeProps.clipboard`.
12+
- breaking: `TreeProps.onClipboardChange` callback now receives `clipboardIds` and `pasteOperation` instead of `clipboard`.
13+
- fix: `TreeProps.onClipboardChange` was previously not always called when the clipboard changes.
14+
315
## 0.7.0
416

517
### Minor Changes

packages/svelte-file-tree/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "svelte-file-tree",
3-
"version": "0.7.0",
3+
"version": "0.8.0",
44
"type": "module",
55
"scripts": {
66
"dev": "svelte-kit sync && svelte-package --watch",

packages/svelte-file-tree/src/lib/components/Tree.svelte

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@
1212
FolderNode,
1313
TreeItemState,
1414
type DefaultTFolder,
15-
type PasteOperation,
1615
} from "$lib/tree.svelte.js";
1716
import { setTreeContext } from "./context.js";
18-
import type { TreeProps } from "./types.js";
17+
import type { PasteOperation, TreeProps } from "./types.js";
1918
2019
let {
2120
children,
@@ -24,7 +23,9 @@
2423
selectedIds = new SvelteSet(defaultSelectedIds),
2524
defaultExpandedIds,
2625
expandedIds = new SvelteSet(defaultExpandedIds),
27-
clipboard = $bindable(),
26+
defaultClipboardIds,
27+
clipboardIds = new SvelteSet(defaultClipboardIds),
28+
pasteOperation = $bindable(),
2829
isItemDisabled = false,
2930
ref = $bindable(null),
3031
copyNode = function copyNode(node): TFile | TFolder {
@@ -80,7 +81,7 @@
8081
parent,
8182
selectedIds: () => selectedIds,
8283
expandedIds: () => expandedIds,
83-
clipboard: () => clipboard,
84+
clipboardIds: () => clipboardIds,
8485
isItemDisabled: () => isItemDisabled,
8586
});
8687
result.push(item);
@@ -240,13 +241,19 @@
240241
}
241242
242243
export function copyToClipboard(itemId: string, operation: PasteOperation) {
243-
const clipboardIds = new SvelteSet(selectedIds);
244+
clipboardIds.clear();
245+
for (const id of selectedIds) {
246+
clipboardIds.add(id);
247+
}
244248
clipboardIds.add(itemId);
245-
clipboard = {
246-
ids: clipboardIds,
247-
operation,
248-
};
249-
onClipboardChange({ clipboard });
249+
pasteOperation = operation;
250+
onClipboardChange({ clipboardIds, pasteOperation });
251+
}
252+
253+
export function clearClipboard() {
254+
clipboardIds.clear();
255+
pasteOperation = undefined;
256+
onClipboardChange({ clipboardIds, pasteOperation });
250257
}
251258
252259
function getNearestAncestor(
@@ -275,13 +282,14 @@
275282
return item.inClipboard;
276283
}
277284
278-
async function copy(clipboardIds: Set<string>, destination: TFolder | TTree) {
285+
async function copy(destination: TFolder | TTree) {
279286
const names = new Set<string>();
280287
for (const child of destination.children) {
281288
names.add(child.name);
282289
}
283290
284291
const sources: Array<TreeItemState<TFile, TFolder>> = [];
292+
const copies: Array<TFile | TFolder> = [];
285293
for (const id of clipboardIds) {
286294
const current = getItem(id);
287295
if (current === undefined) {
@@ -312,20 +320,20 @@
312320
313321
names.add(name);
314322
sources.push(current);
323+
copies.push(copyNode(current.node));
315324
}
316325
317326
if (sources.length === 0) {
318327
return true;
319328
}
320329
321-
const canCopyResult = await canCopy({ sources, destination });
330+
const canCopyResult = await canCopy({ sources, copies, destination });
322331
if (!canCopyResult) {
323332
return false;
324333
}
325334
326335
const destinationChildren = destination.children;
327-
for (const source of sources) {
328-
const copy = copyNode(source.node);
336+
for (const copy of copies) {
329337
destinationChildren.push(copy);
330338
}
331339
onChildrenChange({
@@ -334,7 +342,7 @@
334342
children: destinationChildren,
335343
});
336344
337-
onCopy({ sources, destination });
345+
onCopy({ sources, copies, destination });
338346
return true;
339347
}
340348
@@ -423,41 +431,37 @@
423431
}
424432
425433
export async function paste(destination: TFolder | TTree) {
426-
if (clipboard === undefined) {
427-
return false;
428-
}
429-
430434
let didPaste: boolean;
431-
switch (clipboard.operation) {
435+
switch (pasteOperation) {
432436
case "copy": {
433-
didPaste = await copy(clipboard.ids, destination);
437+
didPaste = await copy(destination);
434438
break;
435439
}
436440
case "cut": {
437-
didPaste = await move(clipboard.ids, isItemInClipboard, destination);
441+
didPaste = await move(clipboardIds, isItemInClipboard, destination);
438442
break;
439443
}
444+
case undefined: {
445+
return false;
446+
}
440447
}
441448
442449
if (!didPaste) {
443450
return false;
444451
}
445452
446-
clipboard = undefined;
453+
clearClipboard();
447454
return true;
448455
}
449456
450457
function onRemoveNode(node: TFile | TFolder) {
451458
const id = node.id;
452459
selectedIds.delete(id);
453460
expandedIds.delete(id);
461+
clipboardIds.delete(id);
454462
455-
if (clipboard !== undefined) {
456-
const clipboardIds = clipboard.ids;
457-
clipboardIds.delete(id);
458-
if (clipboardIds.size === 0) {
459-
clipboard = undefined;
460-
}
463+
if (clipboardIds.size === 0) {
464+
pasteOperation = undefined;
461465
}
462466
463467
if (node.type === "folder") {
@@ -541,10 +545,15 @@
541545
});
542546
}
543547
548+
const currentClipboardSize = clipboardIds.size;
544549
for (const item of removed) {
545550
onRemoveNode(item.node);
546551
}
547552
553+
if (clipboardIds.size !== currentClipboardSize) {
554+
onClipboardChange({ clipboardIds, pasteOperation });
555+
}
556+
548557
onRemove({ removed });
549558
return true;
550559
}
@@ -568,7 +577,7 @@
568577
}
569578
}
570579
571-
setTreeContext<TFile, TFolder>({
580+
setTreeContext<TFile, TFolder, TTree>({
572581
root: () => root,
573582
tabbableId: () => tabbableId ?? root.children[0].id,
574583
getItemElementId,
@@ -763,8 +772,7 @@
763772
}
764773
case "Escape": {
765774
selectedIds.clear();
766-
clipboard = undefined;
767-
onClipboardChange({ clipboard });
775+
clearClipboard();
768776
break;
769777
}
770778
case "*": {
@@ -809,7 +817,7 @@
809817
break;
810818
}
811819
812-
if (clipboard === undefined) {
820+
if (pasteOperation === undefined) {
813821
break;
814822
}
815823
@@ -829,7 +837,7 @@
829837
}
830838
}
831839
832-
if (pasteDestinationItem !== undefined && clipboard.operation === "cut") {
840+
if (pasteDestinationItem !== undefined && pasteOperation === "cut") {
833841
if (pasteDestinationItem.inClipboard) {
834842
onCircularReference({
835843
source: pasteDestinationItem,

packages/svelte-file-tree/src/lib/components/types.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type {
77
FileNode,
88
FileTree,
99
FolderNode,
10-
TreeClipboard,
1110
TreeItemState,
1211
} from "$lib/tree.svelte.js";
1312

@@ -22,8 +21,11 @@ export type TreeChildrenSnippetArgs<
2221
items: Array<TreeItemState<TFile, TFolder>>;
2322
};
2423

24+
export type PasteOperation = "copy" | "cut";
25+
2526
export type OnClipboardChangeArgs = {
26-
clipboard: TreeClipboard | undefined;
27+
clipboardIds: SvelteSet<string>;
28+
pasteOperation: PasteOperation | undefined;
2729
};
2830

2931
export type OnChildrenChangeArgs<
@@ -70,6 +72,7 @@ export type OnCopyArgs<
7072
TTree extends FileTree<TFile | TFolder> = FileTree<TFile | TFolder>,
7173
> = {
7274
sources: Array<TreeItemState<TFile, TFolder>>;
75+
copies: Array<TFile | TFolder>;
7376
destination: TFolder | TTree;
7477
};
7578

@@ -100,7 +103,9 @@ export interface TreeProps<
100103
selectedIds?: SvelteSet<string>;
101104
defaultExpandedIds?: Iterable<string>;
102105
expandedIds?: SvelteSet<string>;
103-
clipboard?: TreeClipboard;
106+
defaultClipboardIds?: Iterable<string>;
107+
clipboardIds?: SvelteSet<string>;
108+
pasteOperation?: PasteOperation;
104109
isItemDisabled?: boolean | ((node: TFile | TFolder) => boolean);
105110
ref?: HTMLElement | null;
106111
copyNode?: (node: TFile | TFolder) => TFile | TFolder;

packages/svelte-file-tree/src/lib/tree.svelte.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,6 @@ export type DefaultTFolder<TFile extends FileNode = FileNode> = FolderNode<
6969
TFile | DefaultTFolder<TFile>
7070
>;
7171

72-
export type PasteOperation = "copy" | "cut";
73-
74-
export type TreeClipboard = {
75-
operation: PasteOperation;
76-
ids: SvelteSet<string>;
77-
};
78-
7972
export type TreeItemStateProps<
8073
TFile extends FileNode = FileNode,
8174
TFolder extends FolderNode<TFile | TFolder> = DefaultTFolder<TFile>,
@@ -86,7 +79,7 @@ export type TreeItemStateProps<
8679
parent?: TreeItemState<TFile, TFolder, TFolder>;
8780
selectedIds: () => SvelteSet<string>;
8881
expandedIds: () => SvelteSet<string>;
89-
clipboard: () => TreeClipboard | undefined;
82+
clipboardIds: () => SvelteSet<string>;
9083
isItemDisabled: () => boolean | ((node: TFile | TFolder) => boolean);
9184
};
9285

@@ -101,7 +94,7 @@ export class TreeItemState<
10194
readonly depth: number;
10295
readonly #selectedIds: () => SvelteSet<string>;
10396
readonly #expandedIds: () => SvelteSet<string>;
104-
readonly #clipboard: () => TreeClipboard | undefined;
97+
readonly #clipboardIds: () => SvelteSet<string>;
10598
readonly #isItemDisabled: () => boolean | ((node: TFile | TFolder) => boolean);
10699

107100
constructor(props: TreeItemStateProps<TFile, TFolder, TNode>) {
@@ -111,7 +104,7 @@ export class TreeItemState<
111104
this.depth = props.parent === undefined ? 0 : props.parent.depth + 1;
112105
this.#selectedIds = props.selectedIds;
113106
this.#expandedIds = props.expandedIds;
114-
this.#clipboard = props.clipboard;
107+
this.#clipboardIds = props.clipboardIds;
115108
this.#isItemDisabled = props.isItemDisabled;
116109
}
117110

@@ -121,10 +114,7 @@ export class TreeItemState<
121114

122115
readonly expanded = $derived.by(() => this.#expandedIds().has(this.node.id));
123116

124-
readonly inClipboard = $derived.by(() => {
125-
const clipboard = this.#clipboard();
126-
return clipboard !== undefined && clipboard.ids.has(this.node.id);
127-
});
117+
readonly inClipboard = $derived.by(() => this.#clipboardIds().has(this.node.id));
128118

129119
readonly disabled = $derived.by(() => {
130120
if (this.parent?.disabled) {

0 commit comments

Comments
 (0)