react-实现一个我们公司产品想要的Tab切换

379 阅读2分钟

最痛苦的事莫过于明明组件库中有现成的,非不要,非得增删改查点功能····

tab切换图片.png 或许这个样式+功能更舒服一点吧。

加的需求:左右两个键可以滚动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;
    }
  }
}

功能实现了,记个笔记,以后再需要就不用写逻辑样式啦=-=。