import type * as React from "react";
import trieMemoize from "trie-memoize";
import type { FileTree } from "./file-tree";
import type { Subject } from "./tree/subject";
import { subject } from "./tree/subject";
/**
* A plugin hook for adding roving focus to file tree nodes.
*
* @param fileTree - A file tree
*/
export function useRovingFocus(
fileTree: FileTree
): UseRovingFocusPlugin {
const focusedNodeId = createFocusedNodeId(fileTree);
return {
didChange: focusedNodeId,
getProps(nodeId) {
return createProps(
focusedNodeId,
nodeId,
nodeId === focusedNodeId.getState()
);
},
};
}
const createProps = trieMemoize(
[WeakMap, Map, Map],
(
focusedNodeId: Subject,
nodeId: number,
focused: boolean
): RovingFocusProps => {
return {
tabIndex: focused ? 0 : -1,
onFocus() {
focusedNodeId.setState(nodeId);
},
onBlur() {
if (focused) {
focusedNodeId.setState(-1);
}
},
};
}
);
const createFocusedNodeId = trieMemoize(
[WeakMap],
(fileTree: FileTree) => subject(-1)
);
export interface RovingFocusProps {
tabIndex: number;
onFocus(e: React.FocusEvent): void;
onBlur(e: React.FocusEvent): void;
}
export interface UseRovingFocusPlugin {
/**
* A subject that you can use to observe to changes to the focused node.
*/
didChange: Subject;
/**
* Get the React props for a given node ID.
*
* @param nodeId - A node ID
*/
getProps: (nodeId: number) => RovingFocusProps;
}