Skip to content

Commit d8c2476

Browse files
committed
refactor(Tabs): 重构标签页 uiwjs#845
1 parent 6302267 commit d8c2476

File tree

3 files changed

+161
-42
lines changed

3 files changed

+161
-42
lines changed

packages/react-tabs/src/Pane.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React from 'react';
22
import { IProps, HTMLDivProps } from '@uiw/utils';
3-
3+
import { TabsPaneWarp } from './style';
44
export interface TabsPaneProps extends IProps, HTMLDivProps {
55
label?: React.ReactNode;
66
disabled?: boolean;
77
}
88

99
export default (props: TabsPaneProps = {}) => {
1010
const { prefixCls = 'w-tabs-pane', className, label: _, ...resetProps } = props;
11-
return <div className={[prefixCls, className].filter(Boolean).join(' ').trim()} {...resetProps} />;
11+
return <TabsPaneWarp className={[prefixCls, className].filter(Boolean).join(' ').trim()} {...resetProps} />;
1212
};

packages/react-tabs/src/index.tsx

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
1+
import React, { useEffect, useState, useRef, useCallback } from 'react';
22
import { IProps, HTMLDivProps } from '@uiw/utils';
33
import Pane from './Pane';
4-
import './style/index.less';
54
import Popover from '@uiw/react-popover';
6-
5+
import * as Styled from './style';
76
export * from './Pane';
87

98
Tabs.Pane = Pane;
109

1110
let labelWidth: number = 0;
1211
export interface TabsProps extends IProps, HTMLDivProps {
13-
prefixCls?: string;
1412
activeKey?: string;
1513
type?: 'default' | 'line' | 'card';
1614
children?: React.ReactNode;
@@ -26,20 +24,11 @@ type FlowNavType = {
2624
};
2725

2826
export default function Tabs(props: TabsProps) {
29-
const {
30-
prefixCls = 'w-tabs',
31-
className,
32-
children,
33-
type = 'default',
34-
activeKey: _,
35-
onTabClick,
36-
...elementProps
37-
} = props;
27+
const { className, children, type = 'default', activeKey: _, onTabClick, ...elementProps } = props;
3828

3929
const [activeKey, setActiveKey] = useState(props.activeKey);
4030
const [slideStyle, setSlideStyle] = useState({ width: 0, left: 0 });
4131
const activeItem = useRef<HTMLDivElement | undefined>();
42-
const cls = [prefixCls, className, type ? `${prefixCls}-${type}` : null].filter(Boolean).join(' ').trim();
4332

4433
const [flowNav, flowNavSet] = useState<FlowNavType>({
4534
content: 0,
@@ -52,7 +41,7 @@ export default function Tabs(props: TabsProps) {
5241
const deviation = 15;
5342

5443
const [nodes, nodesSet] = useState<any>();
55-
const divContentRef = useCallback((node) => {
44+
const divContentRef = useCallback((node: HTMLDivElement | null) => {
5645
if (node !== null) {
5746
nodesSet(nodes);
5847
node.addEventListener('scroll', (e: any) => {
@@ -66,7 +55,7 @@ export default function Tabs(props: TabsProps) {
6655
}
6756
}, []);
6857

69-
const divNavRef = useCallback((node, key: number, itemKey: React.Key | null) => {
58+
const divNavRef = useCallback((node: HTMLDivElement | null, key: number, itemKey: React.Key | null) => {
7059
if (node !== null) {
7160
node.addEventListener('click', (e: any) => {
7261
activeItem.current = node;
@@ -117,40 +106,40 @@ export default function Tabs(props: TabsProps) {
117106
}
118107

119108
return (
120-
<div className={cls} {...elementProps}>
121-
<div style={{ display: 'flex' }}>
122-
<div style={{ overflow: 'hidden' }}>
123-
<div className={`${prefixCls}-bar`} ref={divContentRef}>
124-
<div className={`${prefixCls}-nav`} style={{ width: 'max-content' }}>
109+
<Styled.TabsWarp className={className} {...elementProps}>
110+
<Styled.TabsDivFlex>
111+
<Styled.TabsDivHidden>
112+
<Styled.TabsDivBar ref={divContentRef}>
113+
<Styled.TabsDivNav>
125114
{renderNav(children)}
126-
<div style={slideStyle} className={`${prefixCls}-slide`} />
127-
</div>
128-
</div>
129-
</div>
115+
<Styled.TabsDivSlide style={slideStyle} />
116+
</Styled.TabsDivNav>
117+
</Styled.TabsDivBar>
118+
</Styled.TabsDivHidden>
130119
{hiddenNav.length > 0 && (
131120
<Popover
132121
trigger="click"
133122
placement="bottomRight"
134123
visibleArrow={false}
135124
content={
136-
<div className={`${prefixCls}-nav-hidden`}>
125+
<Styled.TabsNavHidden>
137126
{renderNav(hiddenNav.map((idx) => (children as Array<React.ReactElement>)[idx]))}
138-
</div>
127+
</Styled.TabsNavHidden>
139128
}
140129
>
141-
<div onClick={showHideenNav} className={`${prefixCls}-flow-content`}>
130+
<Styled.TabsFlowContent onClick={showHideenNav}>
142131
<span></span>
143-
</div>
132+
</Styled.TabsFlowContent>
144133
</Popover>
145134
)}
146-
</div>
135+
</Styled.TabsDivFlex>
147136
{React.Children.map(children, (item: any) => {
148137
if (!item || activeKey !== item.key) {
149138
return null;
150139
}
151140
return React.cloneElement(item, Object.assign({}, item.props, {}));
152141
})}
153-
</div>
142+
</Styled.TabsWarp>
154143
);
155144

156145
function renderNav(children: React.ReactNode): React.ReactNode {
@@ -159,14 +148,6 @@ export default function Tabs(props: TabsProps) {
159148
return null;
160149
}
161150
const divProps: HTMLDivProps = {
162-
className: [
163-
`${prefixCls}-item`,
164-
item.key === activeKey ? 'active' : null,
165-
item.props.disabled ? 'disabled' : null,
166-
]
167-
.filter(Boolean)
168-
.join(' ')
169-
.trim(),
170151
children: item.props.label,
171152
};
172153
if (!item.props.disabled) {
@@ -176,7 +157,16 @@ export default function Tabs(props: TabsProps) {
176157
calcSlideStyle();
177158
};
178159
}
179-
return <div key={key} ref={(ref) => divNavRef(ref, key, item.key)} {...divProps} />;
160+
return (
161+
<Styled.TabsItem
162+
type={type}
163+
disabled={item.props.disabled}
164+
active={item.key === activeKey}
165+
key={key}
166+
ref={(ref) => divNavRef(ref, key, item.key)}
167+
{...divProps}
168+
/>
169+
);
180170
});
181171
}
182172
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import styled, { css } from 'styled-components';
2+
import { ThemeVariantValueOptions } from '@uiw/utils';
3+
import { TabsProps } from '../';
4+
5+
export interface TabsStyleProps extends TabsProps, ThemeVariantValueOptions {}
6+
7+
export const TabsWarp = styled.div<TabsStyleProps>`
8+
${(props) => {
9+
if (props.type === 'line') {
10+
return css`
11+
${TabsDivSlide} {
12+
background-color: #108ee9;
13+
box-sizing: border-box;
14+
bottom: 0;
15+
position: absolute;
16+
height: 1px;
17+
left: 0;
18+
width: 20px;
19+
z-index: 1;
20+
transition: all 0.3s;
21+
}
22+
${TabsDivNav}::after {
23+
content: '';
24+
position: relative;
25+
display: block;
26+
border-bottom: 1px solid #d9d9d9;
27+
}
28+
`;
29+
}
30+
if (props.type === 'card') {
31+
return css`
32+
${TabsDivNav}::after {
33+
content: '';
34+
position: relative;
35+
display: block;
36+
border-bottom: 1px solid #d9d9d9;
37+
}
38+
${TabsItem} {
39+
margin: 0;
40+
border: 1px solid #d9d9d9;
41+
border-bottom: 0;
42+
padding: 7px 16px;
43+
border-radius: 4px 4px 0 0;
44+
background: #f9f9f9;
45+
margin-right: 5px;
46+
bottom: -1px;
47+
}
48+
`;
49+
}
50+
return css``;
51+
}}
52+
`;
53+
54+
export const TabsDivFlex = styled.div`
55+
display: flex;
56+
`;
57+
export const TabsDivHidden = styled.div`
58+
overflow: hidden;
59+
`;
60+
export const TabsDivBar = styled.div`
61+
position: relative;
62+
overflow-x: auto;
63+
height: calc(100% + 17px);
64+
`;
65+
export const TabsDivNav = styled.div`
66+
position: relative;
67+
width: max-content;
68+
`;
69+
export const TabsDivSlide = styled.div``;
70+
71+
export const TabsNavHidden = styled.div`
72+
display: flex;
73+
overflow-y: auto;
74+
padding: 5px 10px 5px 5px;
75+
max-height: 200px;
76+
flex-direction: column;
77+
:hover {
78+
background: #d9d9d9;
79+
}
80+
`;
81+
export const TabsFlowContent = styled.div`
82+
margin-left: 5px;
83+
padding: 0px 10px 0px 10px;
84+
box-shadow: 1px 0px 0px #d9d9d9 inset;
85+
cursor: pointer;
86+
`;
87+
interface TabsItemProps {
88+
active: boolean;
89+
disabled: boolean;
90+
type: TabsProps['type'];
91+
}
92+
93+
export const TabsItem = styled.div<TabsItemProps>`
94+
padding: 7px 10px;
95+
display: inline-block;
96+
height: 100%;
97+
font-size: 14px;
98+
box-sizing: border-box;
99+
position: relative;
100+
cursor: pointer;
101+
text-decoration: none;
102+
transition: all 0.3s;
103+
${(props) => {
104+
if (props.active) {
105+
return css`
106+
color: #008ef0;
107+
${props.type === 'card' &&
108+
css`
109+
background: #fff;
110+
z-index: 1;
111+
`}
112+
`;
113+
}
114+
return css``;
115+
}}
116+
${(props) =>
117+
props.disabled &&
118+
css`
119+
cursor: not-allowed;
120+
user-select: none;
121+
color: rgba(0, 0, 0, 0.25);
122+
`}
123+
`;
124+
125+
export const TabsPaneWarp = styled.div``;
126+
127+
TabsWarp.defaultProps = {
128+
defaultTheme: {},
129+
};

0 commit comments

Comments
 (0)