Skip to content

Commit

Permalink
refactor(Tabs): 重构标签页 uiwjs#845
Browse files Browse the repository at this point in the history
  • Loading branch information
cuilanxin committed Jul 23, 2022
1 parent 6302267 commit d8c2476
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 42 deletions.
4 changes: 2 additions & 2 deletions packages/react-tabs/src/Pane.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';
import { IProps, HTMLDivProps } from '@uiw/utils';

import { TabsPaneWarp } from './style';
export interface TabsPaneProps extends IProps, HTMLDivProps {
label?: React.ReactNode;
disabled?: boolean;
}

export default (props: TabsPaneProps = {}) => {
const { prefixCls = 'w-tabs-pane', className, label: _, ...resetProps } = props;
return <div className={[prefixCls, className].filter(Boolean).join(' ').trim()} {...resetProps} />;
return <TabsPaneWarp className={[prefixCls, className].filter(Boolean).join(' ').trim()} {...resetProps} />;
};
70 changes: 30 additions & 40 deletions packages/react-tabs/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
import React, { useEffect, useState, useRef, useCallback } from 'react';
import { IProps, HTMLDivProps } from '@uiw/utils';
import Pane from './Pane';
import './style/index.less';
import Popover from '@uiw/react-popover';

import * as Styled from './style';
export * from './Pane';

Tabs.Pane = Pane;

let labelWidth: number = 0;
export interface TabsProps extends IProps, HTMLDivProps {
prefixCls?: string;
activeKey?: string;
type?: 'default' | 'line' | 'card';
children?: React.ReactNode;
Expand All @@ -26,20 +24,11 @@ type FlowNavType = {
};

export default function Tabs(props: TabsProps) {
const {
prefixCls = 'w-tabs',
className,
children,
type = 'default',
activeKey: _,
onTabClick,
...elementProps
} = props;
const { className, children, type = 'default', activeKey: _, onTabClick, ...elementProps } = props;

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

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

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

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

return (
<div className={cls} {...elementProps}>
<div style={{ display: 'flex' }}>
<div style={{ overflow: 'hidden' }}>
<div className={`${prefixCls}-bar`} ref={divContentRef}>
<div className={`${prefixCls}-nav`} style={{ width: 'max-content' }}>
<Styled.TabsWarp className={className} {...elementProps}>
<Styled.TabsDivFlex>
<Styled.TabsDivHidden>
<Styled.TabsDivBar ref={divContentRef}>
<Styled.TabsDivNav>
{renderNav(children)}
<div style={slideStyle} className={`${prefixCls}-slide`} />
</div>
</div>
</div>
<Styled.TabsDivSlide style={slideStyle} />
</Styled.TabsDivNav>
</Styled.TabsDivBar>
</Styled.TabsDivHidden>
{hiddenNav.length > 0 && (
<Popover
trigger="click"
placement="bottomRight"
visibleArrow={false}
content={
<div className={`${prefixCls}-nav-hidden`}>
<Styled.TabsNavHidden>
{renderNav(hiddenNav.map((idx) => (children as Array<React.ReactElement>)[idx]))}
</div>
</Styled.TabsNavHidden>
}
>
<div onClick={showHideenNav} className={`${prefixCls}-flow-content`}>
<Styled.TabsFlowContent onClick={showHideenNav}>
<span></span>
</div>
</Styled.TabsFlowContent>
</Popover>
)}
</div>
</Styled.TabsDivFlex>
{React.Children.map(children, (item: any) => {
if (!item || activeKey !== item.key) {
return null;
}
return React.cloneElement(item, Object.assign({}, item.props, {}));
})}
</div>
</Styled.TabsWarp>
);

function renderNav(children: React.ReactNode): React.ReactNode {
Expand All @@ -159,14 +148,6 @@ export default function Tabs(props: TabsProps) {
return null;
}
const divProps: HTMLDivProps = {
className: [
`${prefixCls}-item`,
item.key === activeKey ? 'active' : null,
item.props.disabled ? 'disabled' : null,
]
.filter(Boolean)
.join(' ')
.trim(),
children: item.props.label,
};
if (!item.props.disabled) {
Expand All @@ -176,7 +157,16 @@ export default function Tabs(props: TabsProps) {
calcSlideStyle();
};
}
return <div key={key} ref={(ref) => divNavRef(ref, key, item.key)} {...divProps} />;
return (
<Styled.TabsItem
type={type}
disabled={item.props.disabled}
active={item.key === activeKey}
key={key}
ref={(ref) => divNavRef(ref, key, item.key)}
{...divProps}
/>
);
});
}
}
129 changes: 129 additions & 0 deletions packages/react-tabs/src/style/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import styled, { css } from 'styled-components';
import { ThemeVariantValueOptions } from '@uiw/utils';
import { TabsProps } from '../';

export interface TabsStyleProps extends TabsProps, ThemeVariantValueOptions {}

export const TabsWarp = styled.div<TabsStyleProps>`
${(props) => {
if (props.type === 'line') {
return css`
${TabsDivSlide} {
background-color: #108ee9;
box-sizing: border-box;
bottom: 0;
position: absolute;
height: 1px;
left: 0;
width: 20px;
z-index: 1;
transition: all 0.3s;
}
${TabsDivNav}::after {
content: '';
position: relative;
display: block;
border-bottom: 1px solid #d9d9d9;
}
`;
}
if (props.type === 'card') {
return css`
${TabsDivNav}::after {
content: '';
position: relative;
display: block;
border-bottom: 1px solid #d9d9d9;
}
${TabsItem} {
margin: 0;
border: 1px solid #d9d9d9;
border-bottom: 0;
padding: 7px 16px;
border-radius: 4px 4px 0 0;
background: #f9f9f9;
margin-right: 5px;
bottom: -1px;
}
`;
}
return css``;
}}
`;

export const TabsDivFlex = styled.div`
display: flex;
`;
export const TabsDivHidden = styled.div`
overflow: hidden;
`;
export const TabsDivBar = styled.div`
position: relative;
overflow-x: auto;
height: calc(100% + 17px);
`;
export const TabsDivNav = styled.div`
position: relative;
width: max-content;
`;
export const TabsDivSlide = styled.div``;

export const TabsNavHidden = styled.div`
display: flex;
overflow-y: auto;
padding: 5px 10px 5px 5px;
max-height: 200px;
flex-direction: column;
:hover {
background: #d9d9d9;
}
`;
export const TabsFlowContent = styled.div`
margin-left: 5px;
padding: 0px 10px 0px 10px;
box-shadow: 1px 0px 0px #d9d9d9 inset;
cursor: pointer;
`;
interface TabsItemProps {
active: boolean;
disabled: boolean;
type: TabsProps['type'];
}

export const TabsItem = styled.div<TabsItemProps>`
padding: 7px 10px;
display: inline-block;
height: 100%;
font-size: 14px;
box-sizing: border-box;
position: relative;
cursor: pointer;
text-decoration: none;
transition: all 0.3s;
${(props) => {
if (props.active) {
return css`
color: #008ef0;
${props.type === 'card' &&
css`
background: #fff;
z-index: 1;
`}
`;
}
return css``;
}}
${(props) =>
props.disabled &&
css`
cursor: not-allowed;
user-select: none;
color: rgba(0, 0, 0, 0.25);
`}
`;

export const TabsPaneWarp = styled.div``;

TabsWarp.defaultProps = {
defaultTheme: {},
};

0 comments on commit d8c2476

Please sign in to comment.