一个展开和收起的业务组件(React)

index.tsx

typescript 复制代码
import classNames from 'classnames';
import { useBoolean } from 'ahooks';
import { useMemo } from 'react';
import styles from './index.module.less';

type IProps = {
  foldHeight?: string | number;
} & React.HTMLAttributes<HTMLDivElement>;

// 默认折叠高度
const DEFAULT_FOLD_HEIGHT = 200;

/**
 * 用于折叠展开的组件
 * @param props
 * @returns
 */
const FoldBlock = (props: IProps) => {
  const [open, { toggle }] = useBoolean(false);

  const height = useMemo(() => {
    return typeof props?.foldHeight === 'number'
      ? `${props?.foldHeight ?? DEFAULT_FOLD_HEIGHT}px`
      : props?.foldHeight || `${DEFAULT_FOLD_HEIGHT}px`;
  }, [props?.foldHeight]);

  return (
    <div
      className={classNames(styles.foldBlockSwapper, props?.className)}
      style={{ height: open ? 'auto' : height, ...(props?.style || {}) }}
    >
      {props?.children}
      <div className={styles.toggleBox}>
        <div className={styles.splitLine} />
        <div className={styles.toggleBtn} onClick={toggle}>
          {open ? '收起' : '展开'}
          <span className={classNames(styles.toggleTriangle, open && styles.toggleTriangleOpen)} />
        </div>
        <div className={styles.splitLine} />
      </div>
    </div>
  );
};

export default FoldBlock;

index.module.less

css 复制代码
.foldBlockSwapper {
  position: relative;
  padding-bottom: 44px;
  overflow: hidden;

  .toggleBox {
    position: absolute;
    bottom: 0;
    left: 0;
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 44px;
    padding: 0 24px;
    padding-bottom: 16px;
    background-color: #fff;

    .splitLine {
      flex: 1;
      height: 1px;
      background-color: #e0e0e0;
      transform: scaleY(0.5);
    }

    .toggleBtn {
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 5px 26px;
      color: #0068ff;
      cursor: pointer;

      .toggleTriangle {
        display: inline-block;
        width: 6px;
        height: 6px;
        margin-left: 8px;
        border-top: 1px solid #0068ff;
        border-right: 1px solid #0068ff;
        transform: rotate(135deg);
      }

      .toggleTriangleOpen {
        transform: rotate(315deg);
      }
    }
  }
}
相关推荐
布列瑟农的星空2 分钟前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust
Mr Xu_7 分钟前
Vue 3 中计算属性的最佳实践:提升可读性、可维护性与性能
前端·javascript
jerrywus14 分钟前
我写了个 Claude Code Skill,再也不用手动切图传 COS 了
前端·agent·claude
玖月晴空18 分钟前
探索关于Spec 和Skills 的一些实战运用-Kiro篇
前端·aigc·代码规范
子兮曰22 分钟前
深入理解滑块验证码:那些你不知道的防破解机制
前端·javascript·canvas
会一丢丢蝶泳的咻狗1 小时前
Sass实现,蛇形流动布局
前端·css
攀登的牵牛花1 小时前
前端向架构突围系列 - 状态数据设计 [8 - 4]:有限状态机 (FSM) 在复杂前端逻辑中的应用
前端
Lsx_1 小时前
前端视角下认识 AI Agent 和 LangChain
前端·人工智能·agent
早點睡3901 小时前
高级进阶 React Native 鸿蒙跨平台开发:@react-native-community-slider 滑块组件
react native·react.js·harmonyos
我是伪码农1 小时前
Vue 智慧商城项目
前端·javascript·vue.js