Refactor to enforce strictNullChecks
This commit applies `strictNullChecks` to the entire codebase to improve maintainability and type safety. Key changes include: - Remove some explicit null-checks where unnecessary. - Add necessary null-checks. - Refactor static factory functions for a more functional approach. - Improve some test names and contexts for better debugging. - Add unit tests for any additional logic introduced. - Refactor `createPositionFromRegexFullMatch` to its own function as the logic is reused. - Prefer `find` prefix on functions that may return `undefined` and `get` prefix for those that always return a value.
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
import type { ReadOnlyTreeNode } from '../Node/TreeNode';
|
||||
|
||||
export interface TreeViewFilterEvent {
|
||||
readonly action: TreeViewFilterAction;
|
||||
readonly predicate?: TreeViewFilterPredicate;
|
||||
}
|
||||
type TreeViewFilterTriggeredEvent = {
|
||||
readonly action: TreeViewFilterAction.Triggered;
|
||||
readonly predicate: TreeViewFilterPredicate;
|
||||
};
|
||||
|
||||
type TreeViewFilterRemovedEvent = {
|
||||
readonly action: TreeViewFilterAction.Removed;
|
||||
};
|
||||
|
||||
export type TreeViewFilterEvent = TreeViewFilterTriggeredEvent | TreeViewFilterRemovedEvent;
|
||||
|
||||
export enum TreeViewFilterAction {
|
||||
Triggered,
|
||||
@@ -14,14 +20,14 @@ export type TreeViewFilterPredicate = (node: ReadOnlyTreeNode) => boolean;
|
||||
|
||||
export function createFilterTriggeredEvent(
|
||||
predicate: TreeViewFilterPredicate,
|
||||
): TreeViewFilterEvent {
|
||||
): TreeViewFilterTriggeredEvent {
|
||||
return {
|
||||
action: TreeViewFilterAction.Triggered,
|
||||
predicate,
|
||||
};
|
||||
}
|
||||
|
||||
export function createFilterRemovedEvent(): TreeViewFilterEvent {
|
||||
export function createFilterRemovedEvent(): TreeViewFilterRemovedEvent {
|
||||
return {
|
||||
action: TreeViewFilterAction.Removed,
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ export class TreeNodeHierarchy implements HierarchyAccess {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public setParent(parent: TreeNode): void {
|
||||
public setParent(parent: TreeNode | undefined): void {
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,6 @@ import { TreeNodeManager } from '../../Node/TreeNodeManager';
|
||||
export function parseTreeInput(
|
||||
input: readonly TreeInputNodeData[],
|
||||
): TreeNode[] {
|
||||
if (!input) {
|
||||
throw new Error('missing input');
|
||||
}
|
||||
if (!Array.isArray(input)) {
|
||||
throw new Error('input data must be an array');
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export class TreeNodeInitializerAndUpdater implements TreeNodeCollection {
|
||||
public nodesUpdated = new EventSource<QueryableNodes>();
|
||||
|
||||
public updateRootNodes(rootNodesData: readonly TreeInputNodeData[]): void {
|
||||
if (!rootNodesData?.length) {
|
||||
if (!rootNodesData.length) {
|
||||
throw new Error('missing data');
|
||||
}
|
||||
const rootNodes = this.treeNodeParser(rootNodesData);
|
||||
|
||||
@@ -71,8 +71,12 @@ TreeNavigationKeyCodes,
|
||||
};
|
||||
|
||||
function focusPreviousVisibleNode(context: TreeNavigationContext): void {
|
||||
const focusedNode = context.focus.currentSingleFocusedNode;
|
||||
if (!focusedNode) {
|
||||
return;
|
||||
}
|
||||
const previousVisibleNode = findPreviousVisibleNode(
|
||||
context.focus.currentSingleFocusedNode,
|
||||
focusedNode,
|
||||
context.nodes,
|
||||
);
|
||||
if (!previousVisibleNode) {
|
||||
@@ -83,6 +87,9 @@ function focusPreviousVisibleNode(context: TreeNavigationContext): void {
|
||||
|
||||
function focusNextVisibleNode(context: TreeNavigationContext): void {
|
||||
const focusedNode = context.focus.currentSingleFocusedNode;
|
||||
if (!focusedNode) {
|
||||
return;
|
||||
}
|
||||
const nextVisibleNode = findNextVisibleNode(focusedNode, context.nodes);
|
||||
if (!nextVisibleNode) {
|
||||
return;
|
||||
@@ -92,6 +99,9 @@ function focusNextVisibleNode(context: TreeNavigationContext): void {
|
||||
|
||||
function toggleTreeNodeCheckStatus(context: TreeNavigationContext): void {
|
||||
const focusedNode = context.focus.currentSingleFocusedNode;
|
||||
if (!focusedNode) {
|
||||
return;
|
||||
}
|
||||
const nodeState = focusedNode.state;
|
||||
let transaction = nodeState.beginTransaction();
|
||||
if (nodeState.current.checkState === TreeNodeCheckState.Checked) {
|
||||
@@ -104,19 +114,28 @@ function toggleTreeNodeCheckStatus(context: TreeNavigationContext): void {
|
||||
|
||||
function collapseNodeOrFocusParent(context: TreeNavigationContext): void {
|
||||
const focusedNode = context.focus.currentSingleFocusedNode;
|
||||
if (!focusedNode) {
|
||||
return;
|
||||
}
|
||||
const nodeState = focusedNode.state;
|
||||
const parentNode = focusedNode.hierarchy.parent;
|
||||
if (focusedNode.hierarchy.isBranchNode && nodeState.current.isExpanded) {
|
||||
nodeState.commitTransaction(
|
||||
nodeState.beginTransaction().withExpansionState(false),
|
||||
);
|
||||
} else {
|
||||
const parentNode = focusedNode.hierarchy.parent;
|
||||
if (!parentNode) {
|
||||
return;
|
||||
}
|
||||
context.focus.setSingleFocus(parentNode);
|
||||
}
|
||||
}
|
||||
|
||||
function expandNodeOrFocusFirstChild(context: TreeNavigationContext): void {
|
||||
const focusedNode = context.focus.currentSingleFocusedNode;
|
||||
if (!focusedNode) {
|
||||
return;
|
||||
}
|
||||
const nodeState = focusedNode.state;
|
||||
if (focusedNode.hierarchy.isBranchNode && !nodeState.current.isExpanded) {
|
||||
nodeState.commitTransaction(
|
||||
@@ -151,7 +170,10 @@ function findNextNode(node: TreeNode, nodes: QueryableNodes): TreeNode | undefin
|
||||
return nodes.flattenedNodes[index + 1] || undefined;
|
||||
}
|
||||
|
||||
function findPreviousVisibleNode(node: TreeNode, nodes: QueryableNodes): TreeNode | undefined {
|
||||
function findPreviousVisibleNode(
|
||||
node: TreeNode,
|
||||
nodes: QueryableNodes,
|
||||
): TreeNode | undefined {
|
||||
const previousNode = findPreviousNode(node, nodes);
|
||||
if (!previousNode) {
|
||||
return node.hierarchy.parent;
|
||||
|
||||
@@ -14,6 +14,7 @@ export function useTreeQueryFilter(
|
||||
const { nodes } = useCurrentTreeNodes(treeRootRef);
|
||||
|
||||
let isFiltering = false;
|
||||
|
||||
const statesBeforeFiltering = new NodeStateRestorer();
|
||||
statesBeforeFiltering.saveStateBeforeFilter(nodes.value);
|
||||
|
||||
@@ -35,7 +36,7 @@ export function useTreeQueryFilter(
|
||||
currentNodes.flattenedNodes.forEach((node: TreeNode) => {
|
||||
let transaction = node.state.beginTransaction()
|
||||
.withMatchState(false);
|
||||
transaction = statesBeforeFiltering.applyOriginalState(node, transaction);
|
||||
transaction = statesBeforeFiltering.applyStateBeforeFilter(node, transaction);
|
||||
node.state.commitTransaction(transaction);
|
||||
});
|
||||
statesBeforeFiltering.clear();
|
||||
@@ -150,18 +151,20 @@ class NodeStateRestorer {
|
||||
});
|
||||
}
|
||||
|
||||
public applyOriginalState(
|
||||
public applyStateBeforeFilter(
|
||||
node: TreeNode,
|
||||
transaction: TreeNodeStateTransaction,
|
||||
): TreeNodeStateTransaction {
|
||||
if (!this.originalStates.has(node)) {
|
||||
const originalState = this.originalStates.get(node);
|
||||
if (!originalState) {
|
||||
return transaction;
|
||||
}
|
||||
const originalState = this.originalStates.get(node);
|
||||
if (originalState.isExpanded !== undefined) {
|
||||
transaction = transaction.withExpansionState(originalState.isExpanded);
|
||||
}
|
||||
transaction = transaction.withVisibilityState(originalState.isVisible);
|
||||
if (originalState.isVisible !== undefined) {
|
||||
transaction = transaction.withVisibilityState(originalState.isVisible);
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@@ -185,18 +188,13 @@ function setupWatchers(options: {
|
||||
options.nodesRef,
|
||||
],
|
||||
([filterEvent, nodes]) => {
|
||||
if (!filterEvent) {
|
||||
if (filterEvent === undefined) {
|
||||
return;
|
||||
}
|
||||
switch (filterEvent.action) {
|
||||
case TreeViewFilterAction.Triggered:
|
||||
options.onFilterTrigger(filterEvent.predicate, nodes);
|
||||
break;
|
||||
case TreeViewFilterAction.Removed:
|
||||
options.onFilterReset();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown action: ${TreeViewFilterAction[filterEvent.action]}`);
|
||||
if (filterEvent.action === TreeViewFilterAction.Triggered) {
|
||||
options.onFilterTrigger(filterEvent.predicate, nodes);
|
||||
} else {
|
||||
options.onFilterReset();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
Reference in New Issue
Block a user