Skip to content

Commit a622076

Browse files
author
sj
committed
refactor(tree): 重构tree组件 uiwjs#845
1 parent 4866ee2 commit a622076

File tree

3 files changed

+219
-15
lines changed

3 files changed

+219
-15
lines changed

packages/react-tree/src/TreeNode.tsx

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ import { CSSTransition } from 'react-transition-group';
33
import Icon, { IconProps } from '@uiw/react-icon';
44
import { IProps, noop } from '@uiw/utils';
55
import { TreeData, TreeProps, getChildKeys } from './';
6+
import {
7+
CSSTransitionWarp,
8+
TreeNodeUl,
9+
TreeNodeUlLidiv,
10+
TreeNodeUlLidivSpan,
11+
TreeNodeUlLidivSpanIcon,
12+
TreeNodeUlLidivSpanDiv,
13+
} from './style/index';
614

715
interface TreeNodeIconProps {
816
isOpen: boolean;
@@ -21,6 +29,7 @@ interface DisabledObj {
2129
interface TreeNodeProps<T = (data: TreeData, props: TreeNodeIconProps) => IconProps['type']> extends IProps {
2230
data: TreeData[];
2331
level: number;
32+
isOpen?: boolean;
2433
parent?: TreeData;
2534
icon?: T;
2635
iconAnimation?: boolean;
@@ -79,7 +88,8 @@ export default function TreeNode<T>(props: TreeNodeProps<T>) {
7988
}, []);
8089

8190
return (
82-
<CSSTransition
91+
<CSSTransitionWarp
92+
as={CSSTransition}
8393
nodeRef={node}
8494
classNames={prefixCls}
8595
in={isOpen}
@@ -90,8 +100,10 @@ export default function TreeNode<T>(props: TreeNodeProps<T>) {
90100
onEntered={onEntered}
91101
onEntering={onEntering}
92102
>
93-
<ul
103+
<TreeNodeUl
94104
ref={node}
105+
isOpen={isOpen}
106+
level={level}
95107
className={[
96108
level !== 1 && isOpen ? [`${prefixCls}-open`] : null,
97109
level !== 1 && !isOpen ? [`${prefixCls}-close`] : null,
@@ -133,14 +145,19 @@ export default function TreeNode<T>(props: TreeNodeProps<T>) {
133145
}
134146
return (
135147
<li key={idx} style={{ display: item.hideNode ? 'none' : 'block' }}>
136-
<div className={`${prefixCls}-label`}>
137-
<span
148+
<TreeNodeUlLidiv className={`${prefixCls}-label`}>
149+
<TreeNodeUlLidivSpan
138150
style={{ display: noChild ? 'none' : 'auto' }}
139151
className={`${prefixCls}-switcher`}
140-
onClick={(evn) => onItemClick(item, evn)}
152+
onClick={(evn: React.MouseEvent<HTMLElement, MouseEvent>) => onItemClick(item, evn)}
141153
>
142-
<Icon
154+
<TreeNodeUlLidivSpanIcon
155+
as={Icon}
143156
type={iconItem || 'caret-right'}
157+
isIcon={typeof icon}
158+
isNoChild={noChild}
159+
isIconAnimation={iconAnimation}
160+
isItemIsOpen={itemIsOpen}
144161
className={[
145162
typeof icon === 'function' ? `${prefixCls}-switcher-noop` : null,
146163
noChild ? 'no-child' : null,
@@ -151,9 +168,12 @@ export default function TreeNode<T>(props: TreeNodeProps<T>) {
151168
.join(' ')
152169
.trim()}
153170
/>
154-
</span>
155-
<div
171+
</TreeNodeUlLidivSpan>
172+
<TreeNodeUlLidivSpanDiv
156173
onClick={(evn) => disabledObj.onClick?.(item, evn)}
174+
judgeSelected={selected}
175+
judgeisSelected={isSelected}
176+
isDisabled={disabledObj.disabled}
157177
className={[
158178
`${prefixCls}-title`,
159179
selected && isSelected ? 'selected' : null,
@@ -178,8 +198,8 @@ export default function TreeNode<T>(props: TreeNodeProps<T>) {
178198
) : (
179199
<Label label={item.label} className={disabledObj.disabledClass} />
180200
)}
181-
</div>
182-
</div>
201+
</TreeNodeUlLidivSpanDiv>
202+
</TreeNodeUlLidiv>
183203
{item.children && (
184204
<TreeNode
185205
{...other}
@@ -203,7 +223,7 @@ export default function TreeNode<T>(props: TreeNodeProps<T>) {
203223
</li>
204224
);
205225
})}
206-
</ul>
207-
</CSSTransition>
226+
</TreeNodeUl>
227+
</CSSTransitionWarp>
208228
);
209229
}

packages/react-tree/src/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import React, { useEffect, useState } from 'react';
22
import { IconProps } from '@uiw/react-icon';
33
import { IProps, HTMLDivProps, noop } from '@uiw/utils';
44
import TreeNode from './TreeNode';
5-
import './style/index.less';
5+
// import './style/index.less';
6+
import { TreeNodeDiv } from './style/index';
67

78
export type TreeRenderTitleNode = {
89
selected?: boolean;
@@ -219,7 +220,7 @@ export default function Tree(props: TreeProps) {
219220
onChange?.(item.key, selKeys);
220221
}
221222
return (
222-
<div className={cls} {...elementProps}>
223+
<TreeNodeDiv className={cls} {...elementProps}>
223224
<TreeNode
224225
{...{
225226
icon,
@@ -235,6 +236,6 @@ export default function Tree(props: TreeProps) {
235236
data={data}
236237
level={1}
237238
/>
238-
</div>
239+
</TreeNodeDiv>
239240
);
240241
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import styled, { css } from 'styled-components';
2+
import { getThemeVariantValue } from '@uiw/utils';
3+
import { TreeRenderTitleNode, TreeProps } from '../index';
4+
5+
interface CSSTransitionWarpProps extends TreeRenderTitleNode {
6+
defaultTheme?: Record<string, string | number>;
7+
isOpen: boolean;
8+
level: number;
9+
}
10+
interface TreeNodeUlLidivProps {
11+
defaultTheme?: Record<string, string | number>;
12+
}
13+
14+
interface TreeNodeUlLidivSpanIconProps {
15+
defaultTheme?: Record<string, string | number>;
16+
isIcon?: string;
17+
isNoChild?: boolean;
18+
isIconAnimation?: boolean;
19+
isItemIsOpen?: boolean;
20+
}
21+
interface TreeNodeUlLidivSpanDivProps {
22+
judgeSelected?: boolean;
23+
judgeisSelected?: boolean;
24+
isDisabled?: string | null;
25+
}
26+
interface TreeNodeDivProps extends TreeProps {
27+
defaultTheme?: Record<string, string | number>;
28+
}
29+
30+
export const CSSTransitionWarp = styled.div<CSSTransitionWarpProps>`
31+
font-size: ${(props) => getThemeVariantValue(props, 'fontSizeCSSTransitionWarpDefault')};
32+
`;
33+
34+
CSSTransitionWarp.defaultProps = {
35+
defaultTheme: {
36+
fontSizeCSSTransitionWarpDefault: '14px',
37+
},
38+
};
39+
40+
export const TreeNodeUl = styled.ul<CSSTransitionWarpProps>`
41+
padding: 0 !important;
42+
transition: 0.3s all;
43+
overflow: hidden;
44+
margin: 0;
45+
ul {
46+
padding-left: 18px !important;
47+
margin-bottom: 0;
48+
}
49+
li {
50+
list-style: none !important;
51+
& + li {
52+
margin-top: 2px !important;
53+
}
54+
&:first-child {
55+
padding-top: 3px;
56+
}
57+
}
58+
59+
${(props) => props.level !== 1 && props.isOpen && css``}
60+
61+
${(props) =>
62+
props.level !== 1 &&
63+
!props.isOpen &&
64+
css`
65+
height: 0;
66+
`}
67+
`;
68+
69+
export const TreeNodeUlLidiv = styled.div<TreeNodeUlLidivProps>`
70+
line-height: initial;
71+
& > * {
72+
vertical-align: middle;
73+
}
74+
`;
75+
76+
export const TreeNodeUlLidivSpan = styled.div<TreeNodeUlLidivProps>`
77+
cursor: pointer;
78+
position: relative;
79+
z-index: 1;
80+
width: 14px;
81+
height: 14px;
82+
line-height: 14px;
83+
display: inline-block;
84+
text-align: center;
85+
&:hover {
86+
color: ${(props) => getThemeVariantValue(props, 'colorTreeNodeUlLidivSpanDefault')};
87+
}
88+
.w-icon {
89+
transition: 0.3s all;
90+
transform: ${(props) => getThemeVariantValue(props, 'transformTreeNodeUlLidivSpanDefault')};
91+
}
92+
`;
93+
94+
TreeNodeUlLidivSpan.defaultProps = {
95+
defaultTheme: {
96+
colorTreeNodeUlLidivSpanDefault: '#2ea3f4',
97+
transformTreeNodeUlLidivSpanDefault: 'scale(0.79) rotate(0deg)',
98+
},
99+
};
100+
101+
export const TreeNodeUlLidivSpanIcon = styled.div<TreeNodeUlLidivSpanIconProps>`
102+
${(props) =>
103+
props.isNoChild &&
104+
!props.isIcon &&
105+
css`
106+
display: none;
107+
`}
108+
109+
${(props) =>
110+
props.isItemIsOpen &&
111+
props.isIconAnimation &&
112+
css`
113+
transform: ${(props) => getThemeVariantValue(props, 'transformTreeNodeUlLidivSpanIconDefault')};
114+
`}
115+
`;
116+
117+
TreeNodeUlLidivSpanIcon.defaultProps = {
118+
defaultTheme: {
119+
transformTreeNodeUlLidivSpanIconDefault: 'scale(0.79) rotate(90deg) !important;',
120+
},
121+
};
122+
123+
export const TreeNodeUlLidivSpanDiv = styled.div<TreeNodeUlLidivSpanDivProps>`
124+
display: inline-block;
125+
padding: 2px 5px;
126+
cursor: pointer;
127+
128+
${(props) =>
129+
props.judgeSelected &&
130+
props.judgeisSelected &&
131+
css`
132+
background-color: #d5e8fc;
133+
`}
134+
135+
${(props) =>
136+
props.judgeSelected &&
137+
css`
138+
cursor: not-allowed;
139+
`}
140+
141+
> * {
142+
vertical-align: middle;
143+
}
144+
`;
145+
146+
export const TreeNodeDiv = styled.div<TreeNodeDivProps>`
147+
li {
148+
position: relative;
149+
li {
150+
&:before,
151+
&::after {
152+
content: ' ';
153+
border-left: 1px solid #d9d9d9;
154+
left: -12px;
155+
position: absolute;
156+
}
157+
&::after {
158+
height: 100%;
159+
top: 5px;
160+
}
161+
&:last-child::after {
162+
height: 16px;
163+
top: -18px;
164+
}
165+
&:before {
166+
content: ' ';
167+
width: 10px;
168+
height: 16px;
169+
border-bottom: 1px solid #d9d9d9;
170+
top: -2px;
171+
}
172+
&:last-child::before {
173+
border-radius: ${(props) => getThemeVariantValue(props, 'borderRadiusTreeNodeDivDefault')};
174+
}
175+
}
176+
}
177+
`;
178+
179+
TreeNodeDiv.defaultProps = {
180+
defaultTheme: {
181+
borderRadiusTreeNodeDivDefault: '0 0 0 3px;',
182+
},
183+
};

0 commit comments

Comments
 (0)