From cb9699309966f1a6482c21a34d73a77789007dea Mon Sep 17 00:00:00 2001 From: tmunoz Date: Wed, 28 Jun 2023 09:11:05 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8:=20Add=20unselect=20feature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/app/app.page.css | 6 +-- src/features/app/app.page.tsx | 6 +-- src/features/app/components/my-tree.css | 3 ++ src/features/app/components/my-tree.tsx | 37 ++++++++++++++----- src/shared/components/tree/hooks/tree.hook.ts | 7 +++- .../components/tree/models/tree.model.ts | 11 ++++++ .../state/actions/unselect-node.action.ts | 18 +++++++++ .../components/tree/state/tree.reducer.ts | 2 + .../components/tree/state/tree.state.ts | 4 +- src/shared/components/tree/tree.tsx | 21 ++++++++--- 10 files changed, 89 insertions(+), 26 deletions(-) create mode 100644 src/features/app/components/my-tree.css create mode 100644 src/shared/components/tree/state/actions/unselect-node.action.ts diff --git a/src/features/app/app.page.css b/src/features/app/app.page.css index 6336096..05bc480 100644 --- a/src/features/app/app.page.css +++ b/src/features/app/app.page.css @@ -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; -} diff --git a/src/features/app/app.page.tsx b/src/features/app/app.page.tsx index cc98fdc..f95ad24 100644 --- a/src/features/app/app.page.tsx +++ b/src/features/app/app.page.tsx @@ -5,10 +5,8 @@ import { MyTree } from "./components/my-tree"; const App = () => (

TREE COMPONENT

-
- -
+
); -export default App; +export default App; \ No newline at end of file diff --git a/src/features/app/components/my-tree.css b/src/features/app/components/my-tree.css new file mode 100644 index 0000000..558e3a2 --- /dev/null +++ b/src/features/app/components/my-tree.css @@ -0,0 +1,3 @@ +.tree-container { + position: relative; +} \ No newline at end of file diff --git a/src/features/app/components/my-tree.tsx b/src/features/app/components/my-tree.tsx index ae4fede..11acdcb 100644 --- a/src/features/app/components/my-tree.tsx +++ b/src/features/app/components/my-tree.tsx @@ -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(null); + const handleLoad: TreeOnLoadType = useCallback((node) => { const DELAY_IN_MS = 500; const NUMBER_OF_ELEMENTS_FOR_LEVEL = 10; @@ -59,14 +64,26 @@ export const MyTree = () => { [] ); + const handleUnSelect = () => { + treeRef.current?.unSelect(); + } + return ( - + <> +
+ +
+
+ +
+ ); }; diff --git a/src/shared/components/tree/hooks/tree.hook.ts b/src/shared/components/tree/hooks/tree.hook.ts index 6c7587c..e9713f0 100644 --- a/src/shared/components/tree/hooks/tree.hook.ts +++ b/src/shared/components/tree/hooks/tree.hook.ts @@ -74,11 +74,16 @@ export const useTree = ({ onLoad, onRemove, onSelect }: UseTreeProps { + dispatch({type: 'UNSELECT_NODE'}); + } + return { tree, dispatch, handleToggle, handleRemove, - handleSelect + handleSelect, + handleUnSelect }; }; diff --git a/src/shared/components/tree/models/tree.model.ts b/src/shared/components/tree/models/tree.model.ts index 769ad07..73d4ba1 100644 --- a/src/shared/components/tree/models/tree.model.ts +++ b/src/shared/components/tree/models/tree.model.ts @@ -139,3 +139,14 @@ export const selectNode = ( children: rootNode.children.map((rootNode) => selectNode(rootNode, node)), } as TreeNodeInternal; }; + +export const unSelectNode = ( + node: TreeNodeInternal, +): TreeNodeInternal => { + return { + ...node, + selected: false, + children: node.children.map(unSelectNode), + } as TreeNodeInternal; +}; + diff --git a/src/shared/components/tree/state/actions/unselect-node.action.ts b/src/shared/components/tree/state/actions/unselect-node.action.ts new file mode 100644 index 0000000..a351dc6 --- /dev/null +++ b/src/shared/components/tree/state/actions/unselect-node.action.ts @@ -0,0 +1,18 @@ +import { TreeState } from "../tree.state"; +import { unSelectNode } from "../../models/tree.model"; + +export type UnSelectNodeAction = { + type: "UNSELECT_NODE"; +}; + +export const unSelectNodeActionHandler = ( + state: TreeState, +): TreeState => { + + return { + ...state, + nodes: state.nodes.map((rootNode) => + unSelectNode(rootNode) + ), + }; +}; diff --git a/src/shared/components/tree/state/tree.reducer.ts b/src/shared/components/tree/state/tree.reducer.ts index 78bd130..f722a27 100644 --- a/src/shared/components/tree/state/tree.reducer.ts +++ b/src/shared/components/tree/state/tree.reducer.ts @@ -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 = typeof treeReducer; @@ -24,6 +25,7 @@ export const treeReducer = ( MOVE_NODE: moveNodeActionHandler, SET_STATUS_NODE: setStatusNodeActionHandler, SELECT_NODE: selectNodeActionHandler, + UNSELECT_NODE: unSelectNodeActionHandler }; return actionFn[action.type]?.(state, action) ?? state; diff --git a/src/shared/components/tree/state/tree.state.ts b/src/shared/components/tree/state/tree.state.ts index bd6ad4c..359f9e5 100644 --- a/src/shared/components/tree/state/tree.state.ts +++ b/src/shared/components/tree/state/tree.state.ts @@ -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 = { nodes: TreeNodeInternal[]; @@ -18,4 +19,5 @@ export type TreeAction = | RemoveNodeAction | MoveNodeAction | SetStatusNodeAction - | SelectNodeAction; + | SelectNodeAction + | UnSelectNodeAction; diff --git a/src/shared/components/tree/tree.tsx b/src/shared/components/tree/tree.tsx index f09c16c..1753bc4 100644 --- a/src/shared/components/tree/tree.tsx +++ b/src/shared/components/tree/tree.tsx @@ -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 = (_: { level: number; loading: boolean; @@ -27,13 +31,13 @@ type TreeProps = { onSelect: TreeOnSelectType; }; -export function Tree({ +export const Tree = forwardRef(({ nodeHeigth, Node, onDrag, ...props -}: TreeProps) { - const { tree, dispatch, handleToggle, handleRemove, handleSelect } = useTree(props); +}: TreeProps, ref: Ref) => { + const { tree, dispatch, handleToggle, handleRemove, handleSelect, handleUnSelect } = useTree(props); const draggablePropsFn = useTreeDraggable((node, parentNode) => { onDrag(node, parentNode).then(() => { @@ -41,6 +45,13 @@ export function Tree({ }); }); + useImperativeHandle( + ref, + () => ({ + unSelect: handleUnSelect + }) + ) + return ( {({ width, height }) => ( @@ -73,4 +84,4 @@ export function Tree({ )} ); -} +}) as (props: TreeProps & RefAttributes) => JSX.Element;