Skip to content

Commit 318975a

Browse files
committed
Move parent-controlled state from TreeView to the parent.
1 parent 3700fda commit 318975a

File tree

2 files changed

+28
-55
lines changed

2 files changed

+28
-55
lines changed

src/app.tsx

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Terminal } from './terminal';
1010
import { GlasgowFileSystem, type FileTreeNode } from './filesystem';
1111

1212
import { PanelContainer } from './components/panel';
13-
import { TreeView, type TreeViewAPI } from './components/tree-view';
13+
import { TreeView } from './components/tree-view';
1414

1515
import { onlyTruthy } from './helpers/truthy-filter';
1616
import { joinPath } from './helpers/path';
@@ -104,7 +104,9 @@ declare global {
104104
interrupt();
105105
};
106106

107-
let treeViewAPI: TreeViewAPI<FileTreeNode> | null = null;
107+
let [creatingNewFileNode, setCreatingNewFileNode] = createSignal<
108+
Parameters<typeof TreeView<FileTreeNode>>[0]['creatingNewNode']
109+
>(null);
108110

109111
const handleFileTreeNodeAction = async (node: FileTreeNode) => {
110112
let fileContents = await glasgowFS.readFile(node.path);
@@ -128,7 +130,8 @@ declare global {
128130
fileReader.addEventListener('loadend', () => {
129131
const fileContents = new Uint8Array(fileReader.result as ArrayBuffer);
130132

131-
treeViewAPI!.createFile({
133+
setCreatingNewFileNode({
134+
type: 'file',
132135
underNode: node,
133136
defaultName: file.name,
134137
async execute({ node, parents, name, dryRun }) {
@@ -142,7 +145,8 @@ declare global {
142145
};
143146

144147
const createNewFolder = (node: FileTreeNode | null) => {
145-
treeViewAPI!.createFolder({
148+
setCreatingNewFileNode({
149+
type: 'folder',
146150
underNode: node,
147151
async execute({ node, parents, name, dryRun }) {
148152
await glasgowFS.createPath(joinPath(HOME_DIRECTORY, ...parents, node, name), 'folder', null, dryRun);
@@ -159,21 +163,14 @@ declare global {
159163
};
160164

161165
const handleFileDuplicate = async (node: FileTreeNode, parents: FileTreeNode[]) => {
162-
let options: (
163-
| Parameters<NonNullable<typeof treeViewAPI>['createFile']>[0]
164-
| Parameters<NonNullable<typeof treeViewAPI>['createFolder']>[0]
165-
) = {
166+
setCreatingNewFileNode({
167+
type: node.children ? 'folder' : 'file',
166168
underNode: parents.at(-1) ?? null,
167169
defaultName: node.name,
168170
async execute({ name, dryRun }) {
169171
await glasgowFS.duplicatePath(node.path, joinPath(HOME_DIRECTORY, ...parents, name), dryRun);
170172
},
171-
};
172-
if (!node.children) {
173-
treeViewAPI!.createFile(options);
174-
} else {
175-
treeViewAPI!.createFolder(options);
176-
}
173+
});
177174
};
178175

179176
const handleFileRename = async (node: FileTreeNode, parents: FileTreeNode[], newName: string, dryRun: boolean) => {
@@ -255,6 +252,8 @@ declare global {
255252
? (
256253
<TreeView
257254
nodes={fileTree.tree}
255+
creatingNewNode={creatingNewFileNode()}
256+
onCancelNodeCreation={() => setCreatingNewFileNode(null)}
258257
emptyTreeMessage="Directory is empty"
259258
actions={[
260259
{
@@ -302,7 +301,6 @@ declare global {
302301
execute: (node, parents) => handleFileDeletion(node!, parents),
303302
},
304303
]}
305-
api={(value) => treeViewAPI = value}
306304
/>
307305
)
308306
: <i>{isInitializing() ? 'Waiting...' : 'Unavailable'}</i>

src/components/tree-view.tsx

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,6 @@ interface TreeNodeAction<N extends TreeNode> {
2020
showInline?: boolean;
2121
}
2222

23-
export interface TreeViewAPI<N extends TreeNode> {
24-
createFile(options: {
25-
underNode: TreeNode | null;
26-
defaultName?: string;
27-
execute(options: { node: N | null; parents: N[]; name: string; dryRun: boolean; }): Promise<void>;
28-
}): void;
29-
30-
createFolder(options: {
31-
underNode: TreeNode | null;
32-
defaultName?: string;
33-
execute(options: { node: N | null; parents: N[]; name: string; dryRun: boolean; }): Promise<void>;
34-
}): void;
35-
}
36-
3723
interface TreeNodeAPI {
3824
rename(options: {
3925
execute(options: { newName: string; dryRun: boolean; }): Promise<void>;
@@ -44,12 +30,8 @@ interface TreeRootContextValue {
4430
rootNodes: TreeNode[];
4531
nodeElements: Map<TreeNode, HTMLElement>;
4632
currentlyFocusableNode: TreeNode | null;
47-
creatingNewNode: (
48-
(
49-
| Parameters<TreeViewAPI<TreeNode>['createFile']>[0]
50-
| Parameters<TreeViewAPI<TreeNode>['createFolder']>[0]
51-
) & { type: 'file' | 'folder'; }
52-
) | null;
33+
creatingNewNode: TreeViewProps<any>['creatingNewNode'];
34+
cancelNodeCreation: () => void;
5335
actions: TreeNodeAction<TreeNode>[];
5436
focus(node: TreeNode | null): void;
5537
}
@@ -84,14 +66,14 @@ const TreeNodeCreationForm = (props: TreeNodeCreationProps) => {
8466
nameInput.setCustomValidity('');
8567
if (dryRun)
8668
return;
87-
treeRootContext.creatingNewNode = null;
69+
treeRootContext.cancelNodeCreation();
8870
} catch (e) {
8971
nameInput.setCustomValidity(String(e));
9072
}
9173
};
9274

9375
const cancel = () => {
94-
treeRootContext.creatingNewNode = null;
76+
treeRootContext.cancelNodeCreation();
9577
};
9678

9779
const handleBlur = (_event: FocusEvent) => {
@@ -502,16 +484,21 @@ const TreeNodeView = (props: TreeNodeViewProps) => {
502484

503485
interface TreeViewProps<N extends TreeNode> {
504486
nodes: N[];
487+
creatingNewNode: null | {
488+
type: 'file' | 'folder';
489+
underNode: TreeNode | null;
490+
defaultName?: string;
491+
execute(options: { node: N | null; parents: N[]; name: string; dryRun: boolean; }): Promise<void>;
492+
};
505493
emptyTreeMessage?: string;
506494
actions: TreeNodeAction<N>[];
507-
api: (api: TreeViewAPI<N>) => void;
495+
onCancelNodeCreation: () => void;
508496
}
509497

510498
export const TreeView = <N extends TreeNode>(props: TreeViewProps<N>) => {
511499
let rootListRef: HTMLElement | undefined;
512500
const nodeElements = new Map<TreeNode, HTMLElement>();
513501
const [currentlyFocusableNode, setCurrentlyFocusableNode] = createSignal<TreeNode | null>(null);
514-
const [creatingNewNode, setCreatingNewNode] = createSignal<TreeRootContextValue['creatingNewNode']>(null);
515502

516503
createComputed(() => {
517504
props.nodes;
@@ -530,10 +517,10 @@ export const TreeView = <N extends TreeNode>(props: TreeViewProps<N>) => {
530517
},
531518
nodeElements: nodeElements,
532519
get creatingNewNode() {
533-
return creatingNewNode();
520+
return props.creatingNewNode;
534521
},
535-
set creatingNewNode(value) {
536-
setCreatingNewNode(value);
522+
get cancelNodeCreation() {
523+
return props.onCancelNodeCreation;
537524
},
538525
get actions() {
539526
return props.actions as TreeNodeAction<TreeNode>[];
@@ -549,22 +536,10 @@ export const TreeView = <N extends TreeNode>(props: TreeViewProps<N>) => {
549536
},
550537
} satisfies TreeRootContextValue;
551538

552-
const api = {
553-
createFile(options) {
554-
setCreatingNewNode({ ...options, type: 'file' });
555-
},
556-
557-
createFolder(options) {
558-
setCreatingNewNode({ ...options, type: 'folder' });
559-
},
560-
} satisfies TreeViewAPI<N>;
561-
562-
props.api(api);
563-
564539
return (
565540
<TreeRootContext.Provider value={treeRootContextValue}>
566541
<Show
567-
when={props.nodes.length > 0 || creatingNewNode() !== null}
542+
when={props.nodes.length > 0 || props.creatingNewNode !== null}
568543
fallback={<i>{props.emptyTreeMessage ?? 'No entries'}</i>}
569544
>
570545
<TreeList ref={el => rootListRef = el} nodes={props.nodes} parents={[]} />

0 commit comments

Comments
 (0)