-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(tree): add tree component (#51)
affects: @gio-design/components, @gio-design/icons, @gio-design/tokens, website
- Loading branch information
1 parent
0ec777f
commit edb6cda
Showing
21 changed files
with
657 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import * as React from 'react'; | ||
import RcTree, { TreeNode, TreeProps as RcTreeProps } from 'rc-tree'; | ||
import classNames from 'classnames'; | ||
import { DataNode, Key } from 'rc-tree/lib/interface'; | ||
import { ConfigContext } from '../config-provider'; | ||
import renderSwitcherIcon from './iconUtil'; | ||
|
||
export interface GioTreeNodeAttribute { | ||
eventKey: string; | ||
prefixCls: string; | ||
className: string; | ||
expanded: boolean; | ||
selected: boolean; | ||
children: React.ReactNode; | ||
title: React.ReactNode; | ||
pos: string; | ||
isLeaf: boolean; | ||
selectable: boolean; | ||
disabled: boolean; | ||
} | ||
|
||
export interface GioTreeNodeProps { | ||
className?: string; | ||
disabled?: boolean; | ||
title?: string | React.ReactNode; | ||
key?: Key; | ||
eventKey?: string; | ||
isLeaf?: boolean; | ||
checked?: boolean; | ||
expanded?: boolean; | ||
selected?: boolean; | ||
selectable?: boolean; | ||
icon?: ((treeNode: GioTreeNodeAttribute) => React.ReactNode) | React.ReactNode; | ||
children?: React.ReactNode; | ||
[customProp: string]: any; | ||
} | ||
|
||
export type GioTreeNode = React.Component<GioTreeNodeProps, {}>; | ||
|
||
export interface GioTreeNodeBaseEvent { | ||
node: GioTreeNode; | ||
nativeEvent: MouseEvent; | ||
} | ||
|
||
export interface GioTreeNodeSelectedEvent extends GioTreeNodeBaseEvent { | ||
event: 'select'; | ||
selected?: boolean; | ||
selectedNodes?: DataNode[]; | ||
} | ||
|
||
export interface GioTreeNodeExpandedEvent extends GioTreeNodeBaseEvent { | ||
expanded?: boolean; | ||
} | ||
|
||
export interface GioTreeNodeMouseEvent { | ||
node: GioTreeNode; | ||
event: React.DragEvent<HTMLElement>; | ||
} | ||
|
||
export interface GioTreeNodeDragEnterEvent extends GioTreeNodeMouseEvent { | ||
expandedKeys: Key[]; | ||
} | ||
|
||
export type TreeNodeNormal = DataNode; | ||
|
||
export interface TreeProps extends Omit<RcTreeProps, 'prefixCls'> { | ||
className?: string; | ||
/** 是否自动展开父节点 */ | ||
autoExpandParent?: boolean; | ||
/** 是否禁用树 */ | ||
disabled?: boolean; | ||
/** 默认展开所有树节点 */ | ||
defaultExpandAll?: boolean; | ||
/** 默认展开对应树节点 */ | ||
defaultExpandParent?: boolean; | ||
/** 默认展开指定的树节点 */ | ||
defaultExpandedKeys?: Key[]; | ||
/** (受控)展开指定的树节点 */ | ||
expandedKeys?: Key[]; | ||
/** (受控)设置选中的树节点 */ | ||
selectedKeys?: Key[]; | ||
/** 默认选中的树节点 */ | ||
defaultSelectedKeys?: Key[]; | ||
selectable?: boolean; | ||
loadedKeys?: Key[]; | ||
style?: React.CSSProperties; | ||
showIcon?: boolean; | ||
icon?: ((nodeProps: GioTreeNodeAttribute) => React.ReactNode) | React.ReactNode; | ||
switcherIcon?: React.ReactElement<any>; | ||
prefixCls?: string; | ||
children?: React.ReactNode; | ||
} | ||
|
||
interface CompoundedComponent extends React.ForwardRefExoticComponent<TreeProps & React.RefAttributes<RcTree>> { | ||
TreeNode: typeof TreeNode; | ||
} | ||
|
||
const Tree = React.forwardRef<RcTree, TreeProps>((props, ref) => { | ||
const { getPrefixCls, virtual } = React.useContext(ConfigContext); | ||
const { prefixCls: customizePrefixCls, className, showIcon, children, switcherIcon } = props; | ||
|
||
const newProps = { ...props }; | ||
const prefixCls = getPrefixCls('tree', customizePrefixCls); | ||
|
||
return ( | ||
<RcTree | ||
ref={ref} | ||
virtual={virtual} | ||
{...newProps} | ||
prefixCls={prefixCls} | ||
className={classNames(className, { | ||
[`${prefixCls}-icon-hide`]: !showIcon, | ||
})} | ||
switcherIcon={(nodeProps: GioTreeNodeProps) => renderSwitcherIcon(prefixCls, switcherIcon, nodeProps)} | ||
> | ||
{children} | ||
</RcTree> | ||
); | ||
}) as CompoundedComponent; | ||
|
||
Tree.TreeNode = TreeNode; | ||
|
||
Tree.defaultProps = { | ||
showIcon: false, | ||
}; | ||
|
||
export default Tree; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import * as React from 'react'; | ||
import classNames from 'classnames'; | ||
import { CaretDownOutlined } from '@gio-design/icons'; | ||
import { GioTreeNodeProps } from './Tree'; | ||
|
||
export default function renderSwitcherIcon( | ||
prefixCls: string, | ||
switcherIcon: React.ReactNode | null | undefined, | ||
{ isLeaf }: GioTreeNodeProps | ||
) { | ||
if (isLeaf) { | ||
return null; | ||
} | ||
const switcherCls = `${prefixCls}-switcher-icon`; | ||
if (React.isValidElement(switcherIcon)) { | ||
return React.cloneElement(switcherIcon, { | ||
className: classNames(switcherIcon?.props.className || '', switcherCls), | ||
}); | ||
} | ||
|
||
if (switcherIcon) { | ||
return switcherIcon; | ||
} | ||
|
||
return <CaretDownOutlined className={switcherCls} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import Tree from './Tree'; | ||
|
||
export { EventDataNode } from 'rc-tree/lib/interface'; | ||
|
||
export { | ||
TreeProps, | ||
TreeNodeNormal, | ||
GioTreeNode, | ||
GioTreeNodeMouseEvent, | ||
GioTreeNodeExpandedEvent, | ||
GioTreeNodeSelectedEvent, | ||
GioTreeNodeProps, | ||
} from './Tree'; | ||
|
||
export default Tree; |
140 changes: 140 additions & 0 deletions
140
packages/components/src/components/tree/style/index.less
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
@import '../../../stylesheet/theme.less'; | ||
@import '../../../stylesheet/mixin/index.less'; | ||
@import '~@gio-design/tokens/dist/variables.less'; | ||
|
||
@tree-prefix-cls: ~'@{component-prefix}-tree'; | ||
@tree-node-prefix-cls: ~'@{tree-prefix-cls}-treenode'; | ||
|
||
.antTreeFn(@tree-prefix-cls); | ||
|
||
.antTreeFn(@custom-tree-prefix-cls) { | ||
@custom-tree-node-prefix-cls: ~'@{custom-tree-prefix-cls}-treenode'; | ||
.@{custom-tree-prefix-cls} { | ||
.reset-component; | ||
background: @color-background-tree-normal; | ||
border-radius: @radius-border-tree; | ||
transition: background-color 0.3s; | ||
|
||
// =================== Virtual List =================== | ||
&-list-holder-inner { | ||
align-items: flex-start; | ||
} | ||
|
||
// ===================== TreeNode ===================== | ||
.@{custom-tree-node-prefix-cls} { | ||
display: flex; | ||
align-items: flex-start; | ||
padding: 0; | ||
outline: none; | ||
width: 100%; | ||
height: 40px; | ||
line-height: 40px; | ||
border-radius: @radius-border-tree; | ||
margin: 2px 0; | ||
font-size: 14px; | ||
color: @color-text-tree-normal; | ||
font-weight: 400; | ||
&:hover { | ||
background-color: @color-background-tree-hover; | ||
} | ||
// Disabled | ||
&-disabled { | ||
.@{custom-tree-prefix-cls}-node-content-wrapper { | ||
color: @color-text-tree-disable; | ||
cursor: not-allowed; | ||
.@{custom-tree-prefix-cls}-iconEle { | ||
svg { | ||
color: @color-text-tree-disable; | ||
path { | ||
fill: @color-text-tree-disable; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
// Selected | ||
&-selected { | ||
background-color: @color-background-tree-active; | ||
.@{custom-tree-prefix-cls}-node-content-wrapper { | ||
color: @color-text-tree-selected; | ||
.@{custom-tree-prefix-cls}-iconEle { | ||
svg { | ||
color: @color-text-tree-selected; | ||
path { | ||
fill: @color-text-tree-selected; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// >>> Indent | ||
&-indent { | ||
align-self: stretch; | ||
white-space: nowrap; | ||
user-select: none; | ||
|
||
&-unit { | ||
display: inline-block; | ||
width: 24px; | ||
} | ||
} | ||
|
||
// >>> Switcher | ||
& &-switcher { | ||
flex: none; | ||
width: 14px; | ||
height: 14px; | ||
margin: 0; | ||
line-height: 40px; | ||
text-align: center; | ||
margin: 2px 8px 0; | ||
color: @color-text-tree-switcher; | ||
cursor: pointer; | ||
|
||
&-noop { | ||
cursor: default; | ||
} | ||
|
||
svg.@{custom-tree-prefix-cls}-switcher-icon { | ||
color: @color-text-tree-switcher; | ||
path { | ||
fill: @color-text-tree-switcher; | ||
} | ||
} | ||
|
||
&_close { | ||
svg.@{custom-tree-prefix-cls}-switcher-icon { | ||
transform: rotate(-90deg); | ||
} | ||
} | ||
} | ||
|
||
// >>> Title | ||
& &-node-content-wrapper { | ||
width: 100%; | ||
cursor: pointer; | ||
|
||
.@{custom-tree-prefix-cls}-title { | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
white-space: nowrap; | ||
} | ||
|
||
// Icon | ||
.@{custom-tree-prefix-cls}-iconEle { | ||
display: inline-block; | ||
width: 16px; | ||
height: 16px; | ||
line-height: 16px; | ||
margin: 2px 8px 0 0; | ||
text-align: center; | ||
vertical-align: middle; | ||
&:empty { | ||
display: none; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import './index.less'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import * as React from 'react'; | ||
|
||
function SvgCaretDownOutlined(props: React.SVGProps<SVGSVGElement>) { | ||
return ( | ||
<svg viewBox='0 0 14 14' width='1em' height='1em' {...props}> | ||
<defs> | ||
<style /> | ||
</defs> | ||
<g id='caret-down-outlined_svg__\u56FE\u5C42_2' data-name='\u56FE\u5C42 2'> | ||
<g id='caret-down-outlined_svg__\u56FE\u5C42_1-2' data-name='\u56FE\u5C42 1'> | ||
<path | ||
d='M7.39 9.51a.5.5 0 01-.78 0l-3-3.7a.51.51 0 01-.06-.53A.5.5 0 014 5h6a.5.5 0 01.45.28.51.51 0 01-.06.53z' | ||
fill='#323333' | ||
fillRule='evenodd' | ||
id='caret-down-outlined_svg__down-2' | ||
/> | ||
</g> | ||
</g> | ||
</svg> | ||
); | ||
} | ||
|
||
const MemoSvgCaretDownOutlined = React.memo(SvgCaretDownOutlined); | ||
export default MemoSvgCaretDownOutlined; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,15 @@ | ||
import React from 'react'; | ||
import * as React from 'react'; | ||
|
||
function LoadingOutlinedBlack(props: React.SVGProps<SVGSVGElement>) { | ||
function SvgLoadingOutlinedBlack(props: React.SVGProps<SVGSVGElement>) { | ||
return ( | ||
<svg width='16px' height='16px' viewBox='0 0 16 16' {...props}> | ||
<g stroke='none' strokeWidth='1' fill='none' fillRule='evenodd'> | ||
<g transform='translate(0.500000, 0.500000)' stroke='#313E75'> | ||
<circle opacity='0.4' cx='7.5' cy='7.5' r='7.5' /> | ||
<path d='M15,7.5 C15,3.35786438 11.6421356,0 7.5,0' strokeLinecap='round' /> | ||
</g> | ||
<svg width='1em' height='1em' viewBox='0 0 16 16' {...props}> | ||
<g transform='translate(.5 .5)' stroke='#313E75' fill='none' fillRule='evenodd'> | ||
<circle opacity={0.4} cx={7.5} cy={7.5} r={7.5} /> | ||
<path d='M15 7.5A7.5 7.5 0 007.5 0' strokeLinecap='round' /> | ||
</g> | ||
</svg> | ||
); | ||
} | ||
|
||
const MemoLoadingOutlinedBlack = React.memo(LoadingOutlinedBlack); | ||
export default MemoLoadingOutlinedBlack; | ||
const MemoSvgLoadingOutlinedBlack = React.memo(SvgLoadingOutlinedBlack); | ||
export default MemoSvgLoadingOutlinedBlack; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,15 @@ | ||
import React from 'react'; | ||
import * as React from 'react'; | ||
|
||
function LoadingOutlinedWhite(props: React.SVGProps<SVGSVGElement>) { | ||
function SvgLoadingOutlinedWhite(props: React.SVGProps<SVGSVGElement>) { | ||
return ( | ||
<svg width='16px' height='16px' viewBox='0 0 16 16' {...props}> | ||
<g stroke='none' strokeWidth='1' fill='none' fillRule='evenodd'> | ||
<g transform='translate(0.500000, 0.500000)' stroke='#FFFFFF'> | ||
<circle opacity='0.4' cx='7.5' cy='7.5' r='7.5' /> | ||
<path d='M15,7.5 C15,3.35786438 11.6421356,0 7.5,0' strokeLinecap='round' /> | ||
</g> | ||
<svg width='1em' height='1em' viewBox='0 0 16 16' {...props}> | ||
<g transform='translate(.5 .5)' stroke='#FFF' fill='none' fillRule='evenodd'> | ||
<circle opacity={0.4} cx={7.5} cy={7.5} r={7.5} /> | ||
<path d='M15 7.5A7.5 7.5 0 007.5 0' strokeLinecap='round' /> | ||
</g> | ||
</svg> | ||
); | ||
} | ||
|
||
const MemoLoadingOutlinedWhite = React.memo(LoadingOutlinedWhite); | ||
export default MemoLoadingOutlinedWhite; | ||
const MemoSvgLoadingOutlinedWhite = React.memo(SvgLoadingOutlinedWhite); | ||
export default MemoSvgLoadingOutlinedWhite; |
Oops, something went wrong.