最痛苦的事莫过于明明组件库中有现成的,非不要,非得增删改查点功能····
或许这个样式+功能更舒服一点吧。
加的需求:左右两个键可以滚动tab,每个tab标签尺寸保持一致,样式就不说了。
代码如下:(此处引用了antd组件库)
import { Button, Tooltip } from 'antd';
import React, { useEffect, useState } from 'react';
import cx from 'classnames';
import styles from './index.less';
type IProps = {
tabs: RecordItem[]; // tab切换数据源
labelKey: string; // 展示的文本
valueKey: string; // 传递给后端的值
parentState: { // 组件的父元素的尺寸
width?: number;
height?: number;
};
current: number; // 当前选中项
onChange: (index: number) => void; // 切换tab触发事件
TRANSLATE_DISTANCE?: number; // 每次点击前进后退按钮需要改变的距离
};
const ScrollTabs: React.FC<IProps> = ({
tabs,
valueKey,
labelKey,
parentState,
onChange,
current = 0,
TRANSLATE_DISTANCE = 150,
}) => {
const [width, setWidth] = useState<number>(0); // 我们看到的tab的长度,即滚动tab可见区域
const [currentIndex, setCurrentIndex] = useState<number>(current); // 当前选中项的index
const [translateX, setTranslateX] = useState<number>(0); // 滚动的长度
const MAX_WIDTH = tabs.length * TRANSLATE_DISTANCE; // 列表总长度
// 点击最左侧按钮
const handlePrev = () => {
let newX = translateX + TRANSLATE_DISTANCE;
if (newX >= 0) {
newX = 0;
}
setTranslateX(newX);
};
//点击最右侧按钮
const handleNext = () => {
let newX = translateX - TRANSLATE_DISTANCE;
if (Math.abs(newX) + width > MAX_WIDTH) {// 触发这个条件证明右边已经到头了
newX = width - MAX_WIDTH;
}
setTranslateX(newX);
};
// 切换
const handleChangeIndex = (index: number) => {
setCurrentIndex(index);
onChange(index);
};
useEffect(() => {
if (parentState.width) {
setWidth(Math.min(parentState.width - 300, MAX_WIDTH));
if (Math.abs(translateX) + width >= MAX_WIDTH) {
setTranslateX(width - MAX_WIDTH);
}
}
}, [parentState.width, width, translateX, MAX_WIDTH]);
useEffect(() => {
setCurrentIndex(current);
}, [current]);
return (
<div className={styles.scrollTabsWrapper}>
<Button size="small" className={cx(styles.prev, 'radius-4')} onClick={handlePrev}>
<Icon name="icon-arrow-thin-thread" />
</Button>
<div className={styles.scrollbar} style={{ width }}>
<div
className={styles.scrollTabs}
style={{ transform: `translateX(${translateX}px)` }}
id="scrollTabs"
>
{tabs.map((item, index) => (
<div
key={item[valueKey]}
className={cx(currentIndex === index ? styles.active : undefined, styles.btn_item)}
onClick={() => handleChangeIndex(index)}
style={{ width: TRANSLATE_DISTANCE }}
>
<Tooltip title={item[labelKey]}>
<div>{item[labelKey]}</div>
</Tooltip>
</div>
))}
</div>
</div>
<Button size="small" className={cx(styles.next, 'radius-4')} onClick={handleNext}>
<Icon name="icon-arrow-thin-thread" />
</Button>
</div>
);
};
export default ScrollTabs;
css代码
transition: transform 0.3s;
.flex(row);
> div {
.flex(row);
flex-shrink: 0;
width: 150px;
height: 32px;
padding: 0 4px;
line-height: 32px;
text-align: center;
background-color: #fff;
border: 1px solid#D2D3D9;
border-left: 0 none;
cursor: pointer;
&:hover,
&:focus {
background-color: #f8f9ff;
}
&.active {
color: #fff;
background-color: @primary-color;
border-color: @primary-color;
a {
color: #fff !important;
}
}
&:last-child {
border-right: 0 none;
}
& > div:first-child {
flex: 1;
padding: 0 4px;
.ellipsis();
}
& > div:nth-child(2) {
width: 20px;
a {
color: #606266;
}
}
}
&-wrapper {
flex: 1;
.flex(row);
.prev {
border-radius: 4px 0 0 4px;
}
.next {
border-radius: 0 4px 4px 0;
svg {
transform: rotate(180deg);
}
}
.prev,
.next {
color: #999;
&:hover,
&:focus {
background-color: #f8f9ff;
border-color: #d2d3d9;
}
}
.scrollbar {
overflow: hidden;
}
}
}
功能实现了,记个笔记,以后再需要就不用写逻辑样式啦=-=。