Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions src/features/app/app.page.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
main {
display: grid;
height: 100%;
grid-template-rows: auto 1fr;
grid-template-rows: auto max-content 1fr;
padding: 20px;
}

main > div {
position: relative;
}
6 changes: 2 additions & 4 deletions src/features/app/app.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import { MyTree } from "./components/my-tree";
const App = () => (
<main>
<h1>TREE COMPONENT</h1>
<div>
<MyTree />
</div>
<MyTree />
</main>
);

export default App;
export default App;
3 changes: 3 additions & 0 deletions src/features/app/components/my-tree.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.tree-container {
position: relative;
}
37 changes: 27 additions & 10 deletions src/features/app/components/my-tree.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import './my-tree-node.css'
import './my-tree.css'

import { MyTreeNode, MyTreeNodeData } from "./my-tree-node";
import {
Tree,
TreeNodeComponentRef,
TreeOnDragType,
TreeOnLoadType,
TreeOnRemoveType,
TreeOnSelectType,
} from "../../../shared/components/tree";

import { useCallback } from "react";
import { useCallback, useRef } from "react";

export const MyTree = () => {
const treeRef = useRef<TreeNodeComponentRef>(null);

const handleLoad: TreeOnLoadType<MyTreeNodeData> = useCallback((node) => {
const DELAY_IN_MS = 500;
const NUMBER_OF_ELEMENTS_FOR_LEVEL = 10;
Expand Down Expand Up @@ -59,14 +64,26 @@ export const MyTree = () => {
[]
);

const handleUnSelect = () => {
treeRef.current?.unSelect();
}

return (
<Tree
nodeHeigth={40}
Node={MyTreeNode}
onLoad={handleLoad}
onDrag={handleDrag}
onRemove={handleRemove}
onSelect={handleOnSelect}
/>
<>
<div>
<button type="button" onClick={handleUnSelect} >UnSelect</button>
</div>
<div className='tree-container'>
<Tree
ref={treeRef}
nodeHeigth={40}
Node={MyTreeNode}
onLoad={handleLoad}
onDrag={handleDrag}
onRemove={handleRemove}
onSelect={handleOnSelect}
/>
</div>
</>
);
};
7 changes: 6 additions & 1 deletion src/shared/components/tree/hooks/tree.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,16 @@ export const useTree = <TData>({ onLoad, onRemove, onSelect }: UseTreeProps<TDat
onSelect(node);
};

const handleUnSelect = () => {
dispatch({type: 'UNSELECT_NODE'});
}

return {
tree,
dispatch,
handleToggle,
handleRemove,
handleSelect
handleSelect,
handleUnSelect
};
};
11 changes: 11 additions & 0 deletions src/shared/components/tree/models/tree.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,14 @@ export const selectNode = <TData>(
children: rootNode.children.map((rootNode) => selectNode(rootNode, node)),
} as TreeNodeInternal<TData>;
};

export const unSelectNode = <TData>(
node: TreeNodeInternal<TData>,
): TreeNodeInternal<TData> => {
return {
...node,
selected: false,
children: node.children.map(unSelectNode),
} as TreeNodeInternal<TData>;
};

18 changes: 18 additions & 0 deletions src/shared/components/tree/state/actions/unselect-node.action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { TreeState } from "../tree.state";
import { unSelectNode } from "../../models/tree.model";

export type UnSelectNodeAction = {
type: "UNSELECT_NODE";
};

export const unSelectNodeActionHandler = <TData>(
state: TreeState<TData>,
): TreeState<TData> => {

return {
...state,
nodes: state.nodes.map((rootNode) =>
unSelectNode(rootNode)
),
};
};
2 changes: 2 additions & 0 deletions src/shared/components/tree/state/tree.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { removeNodeActionHandler } from "./actions/remove-nodeAction";
import { selectNodeActionHandler } from "./actions/select-node.action";
import { setStatusNodeActionHandler } from "./actions/set-status-node.action";
import { toggleActionHandler } from "./actions/toggle-node.action";
import { unSelectNodeActionHandler } from "./actions/unselect-node.action";
import { TreeAction, TreeState } from "./tree.state";

export type TreeReducer<TData> = typeof treeReducer<TData>;
Expand All @@ -24,6 +25,7 @@ export const treeReducer = <TData>(
MOVE_NODE: moveNodeActionHandler,
SET_STATUS_NODE: setStatusNodeActionHandler,
SELECT_NODE: selectNodeActionHandler,
UNSELECT_NODE: unSelectNodeActionHandler
};

return actionFn[action.type]?.(state, action) ?? state;
Expand Down
4 changes: 3 additions & 1 deletion src/shared/components/tree/state/tree.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SelectNodeAction } from "./actions/select-node.action";
import { SetStatusNodeAction } from "./actions/set-status-node.action";
import { ToggleNodeAction } from "./actions/toggle-node.action";
import { TreeNodeInternal } from "../models/tree.model";
import { UnSelectNodeAction } from './actions/unselect-node.action';

export type TreeState<TData> = {
nodes: TreeNodeInternal<TData>[];
Expand All @@ -18,4 +19,5 @@ export type TreeAction<TData> =
| RemoveNodeAction<TData>
| MoveNodeAction<TData>
| SetStatusNodeAction<TData>
| SelectNodeAction<TData>;
| SelectNodeAction<TData>
| UnSelectNodeAction;
21 changes: 16 additions & 5 deletions src/shared/components/tree/tree.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import React, { Ref, RefAttributes, forwardRef, useImperativeHandle } from "react";
import { TreeOnDragType, useTreeDraggable } from "./hooks/tree-draggable.hook";
import { TreeOnLoadType, TreeOnRemoveType, TreeOnSelectType, useTree } from "./hooks/tree.hook";

import AutoSizer from "react-virtualized-auto-sizer";
import { FixedSizeList } from "react-window";
import React from "react";
import { TreeNode } from "./models/tree.model";

export type TreeNodeComponentRef = {
unSelect: () => void;
}

export type TreeNodeComponent<TData> = (_: {
level: number;
loading: boolean;
Expand All @@ -27,20 +31,27 @@ type TreeProps<TData> = {
onSelect: TreeOnSelectType<TData>;
};

export function Tree<TData>({
export const Tree = forwardRef(<TData extends object>({
nodeHeigth,
Node,
onDrag,
...props
}: TreeProps<TData>) {
const { tree, dispatch, handleToggle, handleRemove, handleSelect } = useTree(props);
}: TreeProps<TData>, ref: Ref<TreeNodeComponentRef>) => {
const { tree, dispatch, handleToggle, handleRemove, handleSelect, handleUnSelect } = useTree(props);

const draggablePropsFn = useTreeDraggable<TData>((node, parentNode) => {
onDrag(node, parentNode).then(() => {
dispatch({ type: "MOVE_NODE", payload: { node, parentNode } });
});
});

useImperativeHandle(
ref,
() => ({
unSelect: handleUnSelect
})
)

return (
<AutoSizer>
{({ width, height }) => (
Expand Down Expand Up @@ -73,4 +84,4 @@ export function Tree<TData>({
)}
</AutoSizer>
);
}
}) as <TData extends object> (props: TreeProps<TData> & RefAttributes<TreeNodeComponentRef>) => JSX.Element;