Skip to content

Commit 58998df

Browse files
author
Abdelrahman
authored
Merge pull request #34 from abdel-17/disable-recursively
Apple `disabled` Prop Recursively
2 parents aceabc0 + efc305f commit 58998df

File tree

10 files changed

+62
-49
lines changed

10 files changed

+62
-49
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
pasteOperation = $bindable(),
2525
id = defaultId,
2626
element = $bindable(null),
27+
editable = false,
28+
disabled = false,
2729
generateCopyId = () => crypto.randomUUID(),
2830
onRenameItem = (args) => {
2931
args.target.name = args.name;
@@ -72,7 +74,12 @@
7274

7375
{#snippet items(nodes: Array<FileTreeNode>)}
7476
{#each nodes as node, index (node.id)}
75-
<TreeItemContextProvider {node} {index}>
77+
<TreeItemContextProvider
78+
{node}
79+
{index}
80+
editable={typeof editable === "function" ? editable(node) : editable}
81+
disabled={typeof disabled === "function" ? disabled(node) : disabled}
82+
>
7683
{#snippet children(args)}
7784
{@render item(args)}
7885

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@
2222
const {
2323
node,
2424
index,
25+
editable,
26+
disabled,
2527
children,
2628
}: {
2729
node: FileTreeNode;
2830
index: number;
31+
editable: boolean;
32+
disabled: boolean;
2933
children: Snippet<[args: TreeItemSnippetArgs]>;
3034
} = $props();
3135
@@ -34,6 +38,8 @@
3438
parent,
3539
node: () => node,
3640
index: () => index,
41+
editable: () => editable,
42+
disabled: () => disabled,
3743
});
3844
setContext(CONTEXT_KEY, context);
3945

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

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,22 +641,28 @@ export type TreeItemContextProps<TNode extends FileTreeNode = FileTreeNode> = {
641641
parent: TreeItemContext<FolderNode> | undefined;
642642
node: () => TNode;
643643
index: () => number;
644+
editable: () => boolean;
645+
disabled: () => boolean;
644646
};
645647

646648
export class TreeItemContext<TNode extends FileTreeNode = FileTreeNode> {
647649
readonly #treeContext: TreeContext;
648-
readonly parent?: TreeItemContext<FolderNode>;
649650
readonly #node: () => TNode;
650651
readonly #index: () => number;
652+
readonly #editable: () => boolean;
653+
readonly #disabled: () => boolean;
654+
readonly parent?: TreeItemContext<FolderNode>;
651655
readonly depth: number;
652656
readonly dropPosition: DropPositionState;
653657
editing: boolean = $state.raw(false);
654658

655659
constructor(props: TreeItemContextProps<TNode>) {
656660
this.#treeContext = props.treeContext;
657-
this.parent = props.parent;
658661
this.#node = props.node;
659662
this.#index = props.index;
663+
this.#editable = props.editable;
664+
this.#disabled = props.disabled;
665+
this.parent = props.parent;
660666
this.depth = props.parent !== undefined ? props.parent.depth + 1 : 0;
661667
this.dropPosition = new DropPositionState(props);
662668
}
@@ -669,6 +675,17 @@ export class TreeItemContext<TNode extends FileTreeNode = FileTreeNode> {
669675
return this.#index();
670676
}
671677

678+
get editable(): boolean {
679+
return this.#editable();
680+
}
681+
682+
readonly disabled: boolean = $derived.by(() => {
683+
if (this.parent?.disabled) {
684+
return true;
685+
}
686+
return this.#disabled();
687+
});
688+
672689
readonly nearestSelectedAncestor: TreeItemContext<FolderNode> | undefined = $derived.by(() => {
673690
if (this.parent === undefined) {
674691
return;
@@ -714,6 +731,10 @@ export class TreeItemSnippetArgs {
714731
return this.#context.depth;
715732
}
716733

734+
get editable(): boolean {
735+
return this.#context.editable;
736+
}
737+
717738
get editing(): boolean {
718739
return this.#context.editing;
719740
}
@@ -722,6 +743,10 @@ export class TreeItemSnippetArgs {
722743
this.#context.editing = value;
723744
}
724745

746+
get disabled(): boolean {
747+
return this.#context.disabled;
748+
}
749+
725750
get dragged(): boolean {
726751
return this.#context.dragged;
727752
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ export interface TreeProps
5858
item: Snippet<[args: TreeItemSnippetArgs]>;
5959
pasteOperation?: PasteOperation;
6060
id?: string;
61+
editable?: boolean | ((node: FileTreeNode) => boolean);
62+
disabled?: boolean | ((node: FileTreeNode) => boolean);
6163
element?: HTMLElement | null;
6264
generateCopyId?: () => string;
6365
onRenameItem?: (args: RenameItemArgs) => MaybePromise<boolean>;

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
1111
let {
1212
children,
13-
editable = false,
14-
disabled = false,
1513
element = $bindable(null),
1614
onfocusin,
1715
onkeydown,
@@ -38,8 +36,6 @@
3836
const attributes = new TreeItemAttributes({
3937
treeContext,
4038
itemContext,
41-
editable: () => editable,
42-
disabled: () => disabled,
4339
});
4440
</script>
4541

packages/svelte-file-tree/src/lib/components/TreeItem/state.svelte.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,15 @@ import type { DropPosition, TreeContext, TreeItemContext } from "../Tree/state.s
77
export type TreeItemAttributesProps = {
88
treeContext: TreeContext;
99
itemContext: TreeItemContext;
10-
editable: () => boolean;
11-
disabled: () => boolean;
1210
};
1311

1412
export class TreeItemAttributes {
1513
readonly #treeContext: TreeContext;
1614
readonly #itemContext: TreeItemContext;
17-
readonly #editable: () => boolean;
18-
readonly #disabled: () => boolean;
1915

2016
constructor(props: TreeItemAttributesProps) {
2117
this.#treeContext = props.treeContext;
2218
this.#itemContext = props.itemContext;
23-
this.#editable = props.editable;
24-
this.#disabled = props.disabled;
2519
}
2620

2721
get id(): string {
@@ -62,7 +56,7 @@ export class TreeItemAttributes {
6256
};
6357

6458
readonly onkeydown: EventHandler<KeyboardEvent, HTMLElement> = (event) => {
65-
if (this.#disabled()) {
59+
if (this.#itemContext.disabled) {
6660
return;
6761
}
6862

@@ -244,7 +238,7 @@ export class TreeItemAttributes {
244238
break;
245239
}
246240
case "F2": {
247-
if (this.#editable()) {
241+
if (this.#itemContext.editable) {
248242
this.#itemContext.editing = true;
249243
}
250244
break;
@@ -298,7 +292,7 @@ export class TreeItemAttributes {
298292
};
299293

300294
readonly onclick: EventHandler<MouseEvent, HTMLElement> = (event) => {
301-
if (this.#disabled()) {
295+
if (this.#itemContext.disabled) {
302296
return;
303297
}
304298

@@ -313,7 +307,7 @@ export class TreeItemAttributes {
313307
};
314308

315309
readonly ondragstart: EventHandler<DragEvent, HTMLElement> = (event) => {
316-
if (this.#disabled()) {
310+
if (this.#itemContext.disabled) {
317311
return;
318312
}
319313

@@ -332,7 +326,7 @@ export class TreeItemAttributes {
332326

333327
readonly ondragover: EventHandler<DragEvent, HTMLElement> = (event) => {
334328
if (
335-
this.#disabled() ||
329+
this.#itemContext.disabled ||
336330
this.#treeContext.draggedId === undefined ||
337331
this.#itemContext.node.selected ||
338332
this.#itemContext.nearestSelectedAncestor !== undefined
@@ -366,7 +360,7 @@ export class TreeItemAttributes {
366360
this.#itemContext.dropPosition.clear();
367361

368362
const draggedId = this.#treeContext.draggedId;
369-
if (this.#disabled() || draggedId === undefined) {
363+
if (this.#itemContext.disabled || draggedId === undefined) {
370364
return;
371365
}
372366

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,5 @@ export interface TreeItemProps
1515
| "tabindex"
1616
> {
1717
children: Snippet;
18-
editable?: boolean;
19-
disabled?: boolean;
2018
element?: HTMLElement | null;
2119
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ export type FileTreeJSON = {
1616
};
1717

1818
export class FileTree {
19+
readonly #children: LinkedState<Array<FileTreeNode>>;
1920
readonly selectedIds: SvelteSet<string>;
2021
readonly expandedIds: SvelteSet<string>;
21-
readonly #children: LinkedState<Array<FileTreeNode>>;
2222

2323
constructor(props: FileTreeProps) {
24+
this.#children = new LinkedState(() => props.children(this));
2425
this.selectedIds = props.selectedIds ?? new SvelteSet(props.defaultSelectedIds);
2526
this.expandedIds = props.expandedIds ?? new SvelteSet(props.defaultExpandedIds);
26-
this.#children = new LinkedState(() => props.children(this));
2727
}
2828

2929
get children(): Array<FileTreeNode> {

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,9 @@
9292
</script>
9393

9494
<main class="p-8">
95-
<Tree {tree} {onRenameError} {onMoveError} {onNameConflict} class="space-y-4">
95+
<Tree {tree} editable {onRenameError} {onMoveError} {onNameConflict} class="space-y-4">
9696
{#snippet item({ node, depth, editing, dragged, dropPosition })}
9797
<TreeItem
98-
editable
9998
draggable
10099
class={[
101100
"relative flex items-center rounded-md border border-neutral-400 p-3 hover:bg-neutral-200 focus:outline-2 focus:outline-offset-2 focus:outline-current active:bg-neutral-300 aria-selected:border-blue-400 aria-selected:bg-blue-100 aria-selected:text-blue-800 aria-selected:active:bg-blue-200",

sites/sveltekit-example/src/routes/+page.svelte

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,39 +54,26 @@
5454
5555
let disabledIds = new SvelteSet<string>();
5656
57-
const disableNode = (node: FileTreeNode): void => {
58-
disabledIds.add(node.id);
59-
};
60-
61-
const enableNode = (node: FileTreeNode): void => {
62-
disabledIds.delete(node.id);
63-
};
64-
65-
const forEachNode = (nodes: Array<FileTreeNode>, callback: (node: FileTreeNode) => void) => {
66-
for (const node of nodes) {
67-
callback(node);
68-
69-
if (node.type === "folder") {
70-
forEachNode(node.children, callback);
71-
}
72-
}
73-
};
74-
7557
const mutate = async ({
7658
affected,
7759
mutation,
7860
}: {
7961
affected: Array<FileTreeNode>;
8062
mutation: () => Promise<unknown>;
8163
}): Promise<void> => {
82-
forEachNode(affected, disableNode);
64+
for (const node of affected) {
65+
disabledIds.add(node.id);
66+
}
67+
8368
try {
8469
await mutation();
8570
} finally {
8671
try {
8772
await invalidate(FILES_DEPENDENCY);
8873
} finally {
89-
forEachNode(affected, enableNode);
74+
for (const node of affected) {
75+
disabledIds.delete(node.id);
76+
}
9077
}
9178
}
9279
};
@@ -236,6 +223,8 @@
236223
<main class="p-8">
237224
<Tree
238225
{tree}
226+
editable
227+
disabled={(node) => disabledIds.has(node.id)}
239228
{onRenameItem}
240229
{onRenameError}
241230
{onMoveItems}
@@ -245,12 +234,9 @@
245234
{onDeleteItems}
246235
class="space-y-4"
247236
>
248-
{#snippet item({ node, depth, editing, dragged, dropPosition })}
249-
{@const disabled = disabledIds.has(node.id)}
237+
{#snippet item({ node, depth, editing, disabled, dragged, dropPosition })}
250238
<TreeItem
251-
editable
252239
draggable
253-
{disabled}
254240
class={[
255241
"relative flex items-center rounded-md border border-neutral-400 p-3 hover:bg-neutral-200 focus:outline-2 focus:outline-offset-2 focus:outline-current active:bg-neutral-300 aria-selected:border-blue-400 aria-selected:bg-blue-100 aria-selected:text-blue-800 aria-selected:active:bg-blue-200",
256242
dragged && "opacity-50",

0 commit comments

Comments
 (0)