Skip to content

Commit

Permalink
Extend type of data to utilize arrays and maps
Browse files Browse the repository at this point in the history
  • Loading branch information
kpustakhod committed Apr 21, 2023
1 parent 6147952 commit e110488
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 37 deletions.
38 changes: 27 additions & 11 deletions src/TreeView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
isBranchSelectedAndHasSelectedDescendants,
getTreeParent,
getTreeNode,
mapTreeViewData,
} from "./utils";

export type NodeId = number;
Expand Down Expand Up @@ -867,6 +868,7 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
function TreeView(
{
data,
// data: treeViewData,
selectedIds,
nodeRenderer,
onSelect = noop,
Expand All @@ -890,10 +892,13 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
},
ref
) {
const treeViewData: TreeViewData = Array.isArray(data)
? mapTreeViewData(data)
: data;
const nodeRefs = useRef({});
const leafRefs = useRef({});
const [state, dispatch] = useTree({
data,
data: treeViewData,
controlledIds: selectedIds,
controlledExpandedIds: expandedIds,
defaultExpandedIds,
Expand Down Expand Up @@ -933,7 +938,7 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
});
}}
onKeyDown={handleKeyDown({
data,
data: treeViewData,
tabbableId: state.tabbableId,
expandedIds: state.expandedIds,
selectedIds: state.selectedIds,
Expand All @@ -949,12 +954,12 @@ const TreeView = React.forwardRef<HTMLUListElement, ITreeViewProps>(
})}
{...other}
>
{getTreeParent(data).children.map((x, index) => (
{getTreeParent(treeViewData).children.map((x, index) => (
<Node
key={x}
data={data}
element={getTreeNode(data, x)}
setsize={getTreeParent(data).children.length}
data={treeViewData}
element={getTreeNode(treeViewData, x)}
setsize={getTreeParent(treeViewData).children.length}
posinset={index + 1}
level={1}
{...state}
Expand Down Expand Up @@ -1328,8 +1333,9 @@ const handleKeyDown = ({
if (event.ctrlKey) {
if (event.key === "a") {
event.preventDefault();
// const dataWithoutRoot = data.filter((x) => x.id !== 0);
const dataWithoutRoot = Array.from(data).map(([, x]) => x).filter((x) => x.id !== 0);
const dataWithoutRoot = Array.from(data)
.map(([, x]) => x)
.filter((x) => x.id !== 0);
const ids = Object.values(dataWithoutRoot)
.map((x) => x.id)
.filter((id) => !disabledIds.has(id));
Expand Down Expand Up @@ -1505,7 +1511,11 @@ const handleKeyDown = ({
break;
case "End": {
event.preventDefault();
const lastAccessible = getLastAccessible(data, getTreeParent(data).id, expandedIds);
const lastAccessible = getLastAccessible(
data,
getTreeParent(data).id,
expandedIds
);
dispatch({
type: treeTypes.focus,
id: lastAccessible,
Expand Down Expand Up @@ -1576,7 +1586,8 @@ const handleKeyDown = ({
continue;
}
if (
getTreeNode(data, currentId).name[0].toLowerCase() === event.key.toLowerCase()
getTreeNode(data, currentId).name[0].toLowerCase() ===
event.key.toLowerCase()
) {
dispatch({
type: treeTypes.focus,
Expand All @@ -1594,7 +1605,12 @@ const handleKeyDown = ({

TreeView.propTypes = {
/** Tree data*/
data: PropTypes.instanceOf(Map<number, INode>).isRequired,
data: (props, propName) => {
if (Array.isArray(props[propName]) || props[propName] instanceof Map) {
return null;
}
return new Error("TreeView data type must me eigther Array or Map.");
},

/** Function called when a node changes its selected state */
onSelect: PropTypes.func,
Expand Down
31 changes: 20 additions & 11 deletions src/TreeView/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const composeHandlers = (
}
};

export const difference = (a: Set<number>, b: Set<number>) => {
const s = new Set<number>();
export const difference = (a: Set<NodeId>, b: Set<NodeId>) => {
const s = new Set<NodeId>();
for (const v of a) {
if (!b.has(v)) {
s.add(v);
Expand Down Expand Up @@ -46,7 +46,7 @@ export const usePreviousData = (value: TreeViewData | undefined) => {
return ref.current;
};

export const isBranchNode = (data: TreeViewData, i: number) => {
export const isBranchNode = (data: TreeViewData, i: NodeId) => {
const node = getTreeNode(data, i);
return node.children != null && node.children.length > 0;
};
Expand Down Expand Up @@ -288,20 +288,25 @@ interface ITreeNode {
name: string;
children?: ITreeNode[];
}
// @ToDo: refactor to use id from ITreeNode if present

export const flattenTree = function(tree: ITreeNode): TreeViewData {
let count = 0;
let internalCount = 0;
const flattenedTree: TreeViewData = new Map<NodeId, INode>();

const flattenTreeHelper = function(tree: ITreeNode, parent: NodeId | null) {
const node: INode = {
id: count,
id: tree.id || internalCount,
name: tree.name,
children: [],
parent,
};
flattenedTree.set(count, node);
count += 1;

if (flattenedTree.has(node.id)) {
throw Error("TreeView node must has unique ids");
}

flattenedTree.set(node.id, node);
internalCount += 1;
if (tree.children == null || tree.children.length === 0) return;
for (const child of tree.children) {
flattenTreeHelper(child, node.id);
Expand Down Expand Up @@ -383,7 +388,7 @@ export const onComponentBlur = (
export const isBranchSelectedAndHasSelectedDescendants = (
data: TreeViewData,
elementId: NodeId,
selectedIds: Set<number>
selectedIds: Set<NodeId>
) => {
return (
isBranchNode(data, elementId) &&
Expand Down Expand Up @@ -414,9 +419,13 @@ export const getTreeParent = (data: TreeViewData): INode => {
export const getTreeNode = (data: TreeViewData, id: NodeId): INode => {
const treeNode = data.get(id);

if (!treeNode) {
throw Error("TreeView data node can't be null.");
if (treeNode == null) {
throw Error(`Node with id=${id} doesn't exist in the tree.`);
}

return treeNode;
};

export const mapTreeViewData = (data: INode[]): TreeViewData => {
return new Map<NodeId, INode>(data.map((node: INode) => [node.id, node]));
};
23 changes: 8 additions & 15 deletions website/docs/examples/MultiSelectCheckboxAsync/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { useEffect, useRef, useState } from "react";
import React, { useRef, useState } from "react";
import { FaSquare, FaCheckSquare, FaMinusSquare } from "react-icons/fa";
import { IoMdArrowDropright } from "react-icons/io";
import { AiOutlineLoading } from "react-icons/ai";
import TreeView from "react-accessible-treeview";
import cx from "classnames";
import "./styles.css";

const treeData = [
const initialData = [
{
name: "",
id: 0,
Expand Down Expand Up @@ -48,28 +48,21 @@ const treeData = [
},
];

const initialData = new Map(treeData.map((node, index) => [index, node]));

function MultiSelectCheckboxAsync() {
const loadedAlertElement = useRef(null);
const [data, setData] = useState(initialData);
const [nodesAlreadyLoaded, setNodesAlreadyLoaded] = useState([]);

useEffect(() => console.log(data), [data]);

const updateTreeData = (list, id, children) => {
const data = Array.from(list).map(([,node]) => {
const data = list.map((node) => {
if (node.id === id) {
node.children = children.map((el) => {
return el.id;
});
}
return node;
});
const newMapData = new Map();
data.concat(children).forEach((node) => newMapData.set(node.id, node));

return newMapData;
return data.concat(children);
};

const onLoadData = ({ element }) => {
Expand All @@ -82,16 +75,16 @@ function MultiSelectCheckboxAsync() {
setData((value) =>
updateTreeData(value, element.id, [
{
name: `Child Node ${value.size}`,
name: `Child Node ${value.length}`,
children: [],
id: value.size,
id: value.length,
parent: element.id,
isBranch: true,
},
{
name: "Another child Node",
children: [],
id: value.size + 1,
id: value.length + 1,
parent: element.id,
},
])
Expand Down Expand Up @@ -220,4 +213,4 @@ const CheckBoxIcon = ({ variant, ...rest }) => {
}
};

export default MultiSelectCheckboxAsync;
export default MultiSelectCheckboxAsync;

0 comments on commit e110488

Please sign in to comment.