Concis组件库封装——Collapse折叠面板

1,320 阅读2分钟

您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

折叠面板Collapse、CollapseItem两个组件实现,Collapse用于接收一些折叠面板全局配置,如手风琴、懒加载等;CollapseItem用于对单个折叠栏进行配置,如禁用、排列、图标,因此核心部分使用useContext将父组件的props的部分子组件所需要的值传递给所有子组件。

组件库文档如下: 在这里插入图片描述 组件提供的API能力如下: 在这里插入图片描述

组件源码如下: Collapse.tsx:

import React, { FC, memo, useState, createContext } from 'react';
import { CollapseProps } from './interfase';
import './style/index.module.less';

export const ctx = createContext<any>({} as any); //顶层通信装置

const Collapse: FC<CollapseProps> = (props) => {
  const {
    children,
    defaultActive,
    accordion,
    noBorder,
    headerAlign = 'left',
    lazyLoad = false,
    toggleCallback,
  } = props;
  const [activeKeyList, setActiveKeyList] = useState<Array<number | string>>(defaultActive || []); //父组件管理选中列表

  const providerList = {
    //父组件状态管理store
    activeKeyList,
    setActiveKeyList,
    accordion,
    headerAlign,
    lazyLoad,
    toggleCallback,
  };

  return (
    <ctx.Provider value={providerList}>
      <div
        className="collapse-box"
        style={noBorder ? {} : { border: '1px solid rgba(229, 230, 235, 1)' }}
      >
        {children}
      </div>
    </ctx.Provider>
  );
};

export default memo(Collapse);

CollapseItem.tsx:

import React, { FC, memo, useMemo, useEffect, useContext } from 'react';
import { CollapseItemProps } from './interfase';
import { CaretDownOutlined, CaretRightOutlined, CaretLeftOutlined } from '@ant-design/icons';
import './style/item.module.less';
import useStateCallback from '../_util/hooks/useStateCallback';
import { ctx } from './index';

const CollapseItem: FC<CollapseItemProps> = (props) => {
  const { children, header, disabled = false, listKey, extra } = props;

  const [contentDomHeight, setContentDomHeight] = useStateCallback(0);
  const [hasOpen, setHasOpen] = useStateCallback(false);

  const { activeKeyList, setActiveKeyList, accordion, headerAlign, lazyLoad, toggleCallback } =
    useContext(ctx); //父组件共享状态

  useEffect(() => {
    //根据默认值展开或收起
    if (activeKeyList.indexOf(Number(listKey)) == -1) {
      setContentDomHeight(0);
    } else {
      setContentDomHeight(
        (document.querySelector('.collapse-item-content') as HTMLElement).scrollHeight + 30,
      );
    }
  }, [activeKeyList]);

  const toggleContent = () => {
    if (disabled) return; //禁用
    let newHeight = contentDomHeight;
    if (newHeight == 0) {
      //展开
      if (lazyLoad && !hasOpen) {
        //首次展开懒加载
        setHasOpen(true, (state: boolean) => {
          newHeight =
            (document.querySelector('.collapse-item-content') as HTMLElement).scrollHeight + 30;
          if (accordion) {
            //手风琴,全部清除再加入
            setActiveKeyList([Number(listKey)]);
            toggleCallback && toggleCallback([Number(listKey)]);
          } else {
            setActiveKeyList((oldAList: Array<string | number>) => {
              toggleCallback && toggleCallback([...[...oldAList, Number(listKey)].sort()]);
              return [...[...oldAList, Number(listKey)].sort()];
            });
          }
          setContentDomHeight(newHeight);
        });
      } else {
        newHeight =
          (document.querySelector('.collapse-item-content') as HTMLElement).scrollHeight + 30;
        if (accordion) {
          //手风琴,全部清除再加入
          setActiveKeyList([Number(listKey)]);
          toggleCallback && toggleCallback([Number(listKey)]);
        } else {
          setActiveKeyList((oldAList: Array<string | number>) => {
            toggleCallback && toggleCallback([...[...oldAList, Number(listKey)].sort()]);
            return [...[...oldAList, Number(listKey)].sort()];
          });
        }
        setContentDomHeight(newHeight);
      }
    } else {
      //收起
      newHeight = 0;
      setActiveKeyList((oldAList: Array<string | number>) => {
        oldAList.splice(
          oldAList.findIndex(
            (item: number | string, i: number | string) => Number(i) + 1 == listKey,
          ),
          1,
        );
        return [...oldAList.sort()];
      });
      setContentDomHeight(newHeight);
    }
  };

  const headerHeight = useMemo(() => {
    //展开高度
    return {
      maxHeight: `${contentDomHeight}px`,
    };
  }, [contentDomHeight]);
  const renderHeader = useMemo(() => {
    if (headerAlign == 'left') {
      return (
        <div
          className="collapse-item-header"
          onClick={toggleContent}
          style={disabled ? { color: '#c9cdd4', cursor: 'not-allowed' } : {}}
        >
          <div className="left">
            <div className="header-icon">
              {headerHeight.maxHeight == '0px' ? <CaretRightOutlined /> : <CaretDownOutlined />}
            </div>
            <div className="header-text">{header}</div>
          </div>
          {extra && <div className="right">{extra}</div>}
        </div>
      );
    } else if (headerAlign == 'right') {
      return (
        <div
          className="collapse-item-header"
          onClick={toggleContent}
          style={disabled ? { color: '#c9cdd4', cursor: 'not-allowed' } : {}}
        >
          <div className="left">
            <div className="header-text">{header}</div>
          </div>
          <div className="right">
            {extra}
            <div className="header-icon">
              {headerHeight.maxHeight == '0px' ? <CaretLeftOutlined /> : <CaretDownOutlined />}
            </div>
          </div>
        </div>
      );
    } else if (headerAlign == 'hide') {
      return (
        <div
          className="collapse-item-header"
          onClick={toggleContent}
          style={disabled ? { color: '#c9cdd4', cursor: 'not-allowed' } : {}}
        >
          <div className="left">
            <div className="header-text">{header}</div>
          </div>
          <div className="right">{extra}</div>
        </div>
      );
    }
  }, [headerAlign, headerHeight, disabled]);
  return (
    <div className="collapse-item">
      {renderHeader}
      <div className="collapse-item-content" style={headerHeight}>
        {lazyLoad ? hasOpen && children : children}
      </div>
    </div>
  );
};

export default memo(CollapseItem);

开源不易,欢迎学习和体验,喜欢请多多支持,有问题请留言。