Skip to content

Commit

Permalink
Add tree component (#1845)
Browse files Browse the repository at this point in the history
* Add generic Tree component from ant design

* Fix drag issue

* Add story for searchable tree

* Update checkbox style

* Add teset for tree render without any error

* Extracted switcher icon

* Update icon for switcher

* Updated story code

* Add types

* Improved the UI and theming

---------

Co-authored-by: Goutham Subramanyam <gsn.goutham@gmail.com>
Co-authored-by: Ajmal Noushad <ajufhn@gmail.com>
  • Loading branch information
3 people authored Oct 20, 2023
1 parent 1e1de40 commit c230969
Show file tree
Hide file tree
Showing 11 changed files with 804 additions and 3 deletions.
58 changes: 58 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,63 @@ export type NoDataProps = {
className?: string;
};

export type TreeProps = {
className?: string;
allowDrop?: boolean;
autoExpandParent?: boolean;
blockNode: boolean;
checkable: boolean;
checkedKeys: string[] | { checked: string[]; halfChecked: string[] };
checkStrictly: boolean;
defaultCheckedKeys: string[];
defaultExpandAll: boolean;
defaultExpandedKeys: string[];
defaultExpandParent: boolean;
defaultSelectedKeys: string[];
disabled: boolean;
draggable:
| boolean
| ((node: DataNode) => boolean)
| {
icon?: React.ReactNode | false;
nodeDraggable?: (node: DataNode) => boolean;
};
expandedKeys: string[];
fieldNames: object;
filterTreeNode: Function;
height: number;
icon: ReactNode;
loadData: Function;
loadedKeys: string[];
multiple: boolean;
selectable: boolean;
selectedKeys: string[];
showIcon: boolean;
showLine:
| boolean
| {
showLeafIcon:
| boolean
| ReactNode
| ((props: AntTreeNodeProps) => ReactNode);
};
switcherIcon: ReactNode | ((props: AntTreeNodeProps) => ReactNode);
titleRender: Function;
treeData: array<{ key; title; children; [disabled, selectable] }>;
virtual: boolean;
onCheck: Function;
onDragEnd: Function;
onDragEnter: Function;
onDragLeave: Function;
onDragOver: Function;
onDragStart: Function;
onDrop: Function;
onExpand: Function;
onLoad: Function;
onRightClick: Function;
onSelect: Function;
};

// components

export const Accordion: React.FC<AccordionProps> & {
Expand Down Expand Up @@ -660,3 +717,4 @@ export const Tooltip: React.ForwardRefExoticComponent<TooltipProps>;
export const Popover: React.ForwardRefExoticComponent<PopoverProps>;
export const Kbd: React.FC<KbdProps>;
export const NoData: React.FC<NoDataProps>;
export const Tree: React.FC<TreeProps>;
8 changes: 8 additions & 0 deletions src/components/Tree/SwitcherIcon.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from "react";

import { Down, Right } from "neetoicons";

const SwitcherIcon = ({ expanded }) =>
expanded ? <Down size={16} /> : <Right size={16} />;

export default SwitcherIcon;
168 changes: 168 additions & 0 deletions src/components/Tree/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import React from "react";

import { ConfigProvider, Tree as TreeComponent } from "antd";
import classnames from "classnames";
import PropTypes from "prop-types";

import { ANT_DESIGN_GLOBAL_TOKEN_OVERRIDES } from "utils";

import SwitcherIcon from "./SwitcherIcon";

const Tree = props => (
<ConfigProvider
theme={{
token: { ...ANT_DESIGN_GLOBAL_TOKEN_OVERRIDES },
components: {
Tree: {
directoryNodeSelectedBg: "rgb(var(--neeto-ui-primary-500))",
directoryNodeSelectedColor: "rgb(var(--neeto-ui-white))",
nodeHoverBg: "rgb(var(--neeto-ui-gray-100))",
nodeSelectedBg: "rgb(var(--neeto-ui-primary-100))",
},
},
}}
>
<TreeComponent
switcherIcon={SwitcherIcon}
{...props}
className={classnames("neeto-ui-tree", props.className)}
/>
</ConfigProvider>
);

Tree.propTypes = {
/**
* Whether to allow dropping on the node
*/
allowDrop: PropTypes.bool,
/**
* Whether to automatically expand a parent treeNode
*/
autoExpandParent: PropTypes.bool,
/**
* Whether treeNode fill remaining horizontal space
*/
blockNode: PropTypes.bool,
/**
* Add a Checkbox before the treeNodes.
*/
checkable: PropTypes.bool,
/**
* Specifies the keys of the checked treeNodes
*/
checkedKeys: PropTypes.arrayOf(PropTypes.string),
/**
* Check treeNode precisely; parent treeNode and children treeNodes are not associated
*/
checkStrictly: PropTypes.bool,
/**
* Specifies the keys of the default checked treeNodes.
*/
defaultCheckedKeys: PropTypes.arrayOf(PropTypes.string),
/**
* Specify the keys of the default expanded treeNodes.
*/
defaultExpandedKeys: PropTypes.arrayOf(PropTypes.string),
/**
* Specifies the keys of the default selected treeNodes.
*/
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
/**
* Callback function for when the onCheck event occurs.
*/
onCheck: PropTypes.func,
/**
* Whether disabled the tree
*/
disabled: PropTypes.bool,
/**
* The treeNodes data Array, if set it then you need not to construct children TreeNode. (key should be unique across the whole array).
*/
treeData: PropTypes.array,
/**
* Callback function for when the user clicks a treeNode.
*/
onSelect: PropTypes.func,
/**
* Shows a connecting line.
*/
showLine: PropTypes.bool,
/**
* Config virtual scroll height. Will not support horizontal scroll when enable this.
*/
height: PropTypes.number,
/**
* Specifies whether this Tree or the node is draggable.
*/
draggable: PropTypes.bool,
/**
* Callback function for when the onDragEnter event occurs
*/
onDragEnter: PropTypes.func,
/**
* Callback function for when the onDragStart event occurs
*/
onDragStart: PropTypes.func,
/**
* Callback function for when the onDragEnter event occurs
*/
onDragEnd: PropTypes.func,
/**
* Callback function for when the onDragLeave event occurs
*/
onDragLeave: PropTypes.func,
/**
* Callback function for when the onDragOver event occurs
*/
onDragOver: PropTypes.func,
/**
* Callback function for when the onDrop event occurs
*/
onDrop: PropTypes.func,
/**
* Defines a function to filter (highlight) treeNodes. When the function returns true, the corresponding treeNode will be highlighted
*/
filterTreeNode: PropTypes.func,
/**
* Load data asynchronously
*/
loadData: PropTypes.func,
/**
* Set loaded tree nodes. Need work with loadData
*/
loadedKeys: PropTypes.arrayOf(PropTypes.string),
/**
* Whether can be selected
*/
selectable: PropTypes.bool,
/**
* Specifies the keys of the selected treeNodes, multiple selection needs to set multiple to true
*/
selectedKeys: PropTypes.arrayOf(PropTypes.string),
/**
* Allows selecting multiple treeNodes
*/
multiple: PropTypes.bool,
/**
* Customize tree node title render
*/
titleRender: PropTypes.func,
/**
* Disable virtual scroll when set to false
*/
virtual: PropTypes.bool,
/**
* Callback function for when a treeNode is expanded or collapsed
*/
onExpand: PropTypes.func,
/**
* Callback function for when a treeNode is loaded
*/
onLoad: PropTypes.func,
/**
* Customize collapse/expand icon of tree node
*/
switcherIcon: PropTypes.node,
};

export default Tree;
46 changes: 46 additions & 0 deletions src/styles/components/_tree.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.neeto-ui-tree {
.site-tree-search-value {
color: #f50;
}

.ant-tree-checkbox {
.ant-tree-checkbox-inner {
color: rgb(var(--neeto-ui-primary-500));
border-color: rgb(var(--neeto-ui-gray-300));
border-radius: var(--neeto-ui-rounded-sm);

&::after {
top: 47.5%;
inset-inline-start: 18.5%;
}
}

&:hover {
.ant-tree-checkbox-inner {
border-color: rgb(var(--neeto-ui-gray-500)) !important;
}
}
}

.ant-tree-checkbox-checked {
.ant-tree-checkbox-inner {
border-color: rgb(var(--neeto-ui-primary-500)) !important;
background-color: rgb(var(--neeto-ui-primary-500)) !important;
}
&:hover {
.ant-tree-checkbox-inner {
border-color: rgb(var(--neeto-ui-primary-500)) !important;
}
}
}

.ant-tree-switcher {
display: flex;
align-items: center;
justify-content: center;
}

.ant-tree-draggable-icon {
opacity: 0.4 !important;
}
}
1 change: 1 addition & 0 deletions src/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
@import "./components/kbd";
@import "./components/popover";
@import "./components/no-data";
@import "./components/tree";

//Layouts
@import "./layout/common";
Expand Down
23 changes: 23 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,26 @@ export const showScrollAndRemoveMargin = () => {
document.body.style.overflow = "auto";
document.body.style.marginRight = "0px";
};

export const ANT_DESIGN_GLOBAL_TOKEN_OVERRIDES = {
colorBgContainer: "rgb(var(--neeto-ui-white))",
colorBorderSecondary: "rgb(var(--neeto-ui-gray-200))",
colorFillAlter: "rgb(var(--neeto-ui-gray-100))",
colorFillContent: "rgb(var(--neeto-ui-gray-100))",
colorFillSecondary: "rgb(var(--neeto-ui-gray-100))",
colorIcon: "rgb(var(--neeto-ui-gray-700))",
colorIconHover: "rgb(var(--neeto-ui-gray-800))",
colorLink: "rgb(var(--neeto-ui-primary-500))",
colorLinkActive: "rgb(var(--neeto-ui-primary-800))",
colorLinkHover: "rgb(var(--neeto-ui-primary-600))",
colorPrimary: "rgb(var(--neeto-ui-primary-500))",
colorSplit: "rgb(var(--neeto-ui-gray-100))",
colorText: "rgb(var(--neeto-ui-gray-800))",
colorTextDescription: "rgb(var(--neeto-ui-gray-700))",
colorTextDisabled: "rgb(var(--neeto-ui-gray-600))",
colorTextHeading: "rgb(var(--neeto-ui-black))",
colorTextPlaceholder: "rgb(var(--neeto-ui-gray-500))",
controlItemBgActive: "rgb(var(--neeto-ui-primary-100))",
controlItemBgActiveHover: "rgb(var(--neeto-ui-pastel-purple))",
controlItemBgHover: "rgb(var(--neeto-ui-gray-100))",
};
Loading

0 comments on commit c230969

Please sign in to comment.