前端项目实战之Web端常见的侧边导航栏组件封装

前言:前端B端开发,web常常会用到导航栏,下面进行封装

一、组件封装

  • 组件名: Navigation
  • 入口文件:index.tsx
  • 样式文件:index.module.less
index.tsx
import type { CSSProperties } from 'react';
import { useState } from 'react';
import { useCallback, useEffect } from 'react';
import cs from 'classnames';

import Style from './index.module.less';

export interface NavListType {
  title: string;
  key?: string | number;
}

interface PropsType {
  navList: NavListType[];
  refContent: React.LegacyRef<HTMLElement> | any;
  style?: CSSProperties;
  className?: string;
}

/**
 * 详情通用导航栏
 * @param props
 * @returns
 */
const Navigation = (props: PropsType) => {
  const { navList = [], refContent = null, style, className } = props;
  const [activeIndex, setActiveIndex] = useState<number>(0);

  //  导航点击处理
  const handleAnchorSelect = useCallback(
    (e: number) => {
      if (e !== activeIndex) {
        setTimeout(() => {
          setActiveIndex(e);
        }, 10);
      }
      const parent = refContent?.current;
      const parentY = parent.getBoundingClientRect().y;
      const child = parent?.children[e];
      if (child) {
        const scrollY = child?.getBoundingClientRect().y - parentY;
        parent?.scrollBy(0, scrollY);
      }
    },
    [activeIndex, refContent, setActiveIndex]
  );

  //  滚动监听
  const onScroll = useCallback(() => {
    const { clientHeight, children, scrollTop, scrollHeight } = refContent?.current || {};
    if (children && !!children?.length && clientHeight) {
      for (let i = 0; i < children?.length; i++) {
        const scrollY = children[i]?.getBoundingClientRect()?.y;
        const thisHeight = children[i]?.clientHeight;
        // 3. 其他的,只要出现在屏幕里面就显示
        if (
          scrollY < clientHeight &&
          scrollY + thisHeight > 0 &&
          scrollY + thisHeight <= clientHeight
        )
          setActiveIndex(i);
      }
      // 1. 如果滚动到顶部,设置第一个
      if (scrollTop === 0) {
        setActiveIndex(0);
      }
      // 2. 如果滚动到底部,设置最后一个
      const lastFloatHeight = 30; // 浮动值,距离底部30,不用到底就选中最后一个
      if (scrollHeight - lastFloatHeight <= scrollTop + clientHeight) {
        setActiveIndex(children?.length - 1);
      }
    }
  }, [refContent, setActiveIndex]);

  // 滚动监听挂载
  useEffect(() => {
    window.addEventListener('scroll', onScroll, true);
  }, [onScroll]);

  return (
    <div className={cs(Style.navigationComp, className)} style={style}>
      <div>
        {navList.map((item, index) => {
          const { title, key } = item;
          return (
            <div
              key={key || `${+index}`}
              className={cs(
                Style.navigationItem,
                index === activeIndex && Style.navigationItem_active
              )}
              onClick={() => {
                handleAnchorSelect(index);
              }}
            >
              {title}
            </div>
          );
        })}
      </div>
    </div>
  );
};

export default Navigation;

index.module.less
.navigationComp {
  box-sizing: border-box;
  width: 200px;

  & > div {
    border-left: 1px solid rgba(23, 31, 38, 0.1);

    .navigationItem {
      margin-bottom: 8px;
      height: 26px;
      line-height: 26px;
      padding-left: 12px;
      box-sizing: border-box;
      cursor: pointer;
    }

    .navigationItem_active {
      color: #0068FF;
      border-left: 2px solid #0068FF;
      margin-left: -2px;
      cursor: pointer;
    }
  }
}

二、组件使用

该组件比较简单,使用也比较简单,就不举例了,不懂可以到评论去问哦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值