Skip to content

Commit

Permalink
use filter working great
Browse files Browse the repository at this point in the history
  • Loading branch information
jameskerr committed Apr 4, 2024
1 parent 2d4b3c4 commit 9179af3
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 50 deletions.
10 changes: 10 additions & 0 deletions modules/react-arborist/src/filter/match.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NodeObject } from "../nodes/types";

export function matchesStringProperties<T>(node: NodeObject<T>, term: string) {
const haystack = Array.from(Object.values(node.sourceData as any))
.filter((value) => typeof value === "string")
.join(" ")
.toLocaleLowerCase();

return haystack.includes(term.toLocaleLowerCase());
}
43 changes: 43 additions & 0 deletions modules/react-arborist/src/filter/tree-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { NodeObject } from "../nodes/types";
import { matchesStringProperties } from "./match";
import { FilterOptions } from "./use-filter";

type BoolMap = Record<string, boolean>;

export class TreeFilter<T> {
term: string;
isMatch: (node: NodeObject<T>, term: string) => boolean;

constructor(opts: FilterOptions<T>) {
this.term = (opts.term || "").trim();
this.isMatch = opts.isMatch || matchesStringProperties;
}

get isPresent() {
return this.term.length > 0;
}

getVisibility(nodes: NodeObject<T>[], value: BoolMap = {}) {
for (const node of nodes) {
if (this.isMatch(node, this.term)) {
for (const id of [node.id, ...this.ancestorIds(node)]) {
value[id] = true;
}
} else {
value[node.id] = false;
}
if (node.children) this.getVisibility(node.children, value);
}
return value;
}

ancestorIds(node: NodeObject<T>) {
const ids = [];
let parent = node.parent;
while (parent) {
ids.push(parent.id);
parent = parent.parent;
}
return ids;
}
}
35 changes: 35 additions & 0 deletions modules/react-arborist/src/filter/use-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useState } from "react";
import { OpensOnChangeEvent, OpensState } from "../opens/types";
import { TreeFilter } from "./tree-filter";
import { NodeObject } from "../nodes/types";

export type FilterOptions<T> = {
term?: string;
isMatch?: (node: NodeObject<T>, term: string) => boolean;
openByDefault?: (isFiltered: boolean) => boolean;
};

export function useFilter<T>(
nodes: NodeObject<T>[],
options: FilterOptions<T> = {},
) {
const filter = new TreeFilter(options);
const [opens, setOpens] = useState<OpensState>({});
const [filteredOpens, setFilteredOpens] = useState<OpensState>({});

return {
visible: {
value: filter.isPresent ? filter.getVisibility(nodes) : {},
onChange: () => {},
},
opens: {
value: filter.isPresent ? filteredOpens : opens,
onChange: (e: OpensOnChangeEvent) => {
filter.isPresent ? setFilteredOpens(e.value) : setOpens(e.value);
},
},
openByDefault: options.openByDefault
? options.openByDefault(filter.isPresent)
: true,
};
}
2 changes: 1 addition & 1 deletion modules/react-arborist/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ export * from "./selection/use-multi-selection";
export * from "./dnd/use-dnd";

/* Partial Controllers */
export * from "./visible/use-filter";
export * from "./filter/use-filter";
2 changes: 1 addition & 1 deletion modules/react-arborist/src/props/use-default-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function useDefaultProps<T>(
shortcuts: props.shortcuts ?? defaultShortcuts,
commands: props.commands ?? defaultCommands,

/* Dimentions */
/* Dimensions */
width: props.width ?? 300,
height: props.height ?? 500,
indent: props.indent ?? 24,
Expand Down
44 changes: 0 additions & 44 deletions modules/react-arborist/src/visible/use-filter.ts

This file was deleted.

8 changes: 4 additions & 4 deletions modules/showcase/pages/version4.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ import { gmailData } from "../data/gmail";
import { useState } from "react";

export default function Version4() {
const [searchTerm, setSearchTerm] = useState("");
const [term, setSearchTerm] = useState("");
const nodes = useNodes(gmailData);
const visible = useFilter(nodes.value, searchTerm);
const filterProps = useFilter(nodes.value, { term });

return (
<div className="wrap">
<div className="region flow">
<TreeView
nodes={nodes}
visible={visible}
className="tree"
rowClassName="tree-row"
height={900}
{...filterProps}
/>
<input
type="search"
value={searchTerm}
value={term}
placeholder="Search tree..."
onChange={(e) => setSearchTerm(e.target.value)}
/>
Expand Down

0 comments on commit 9179af3

Please sign in to comment.