美文网首页
多个固定列的React table组件

多个固定列的React table组件

作者: 景阳冈大虫在此 | 来源:发表于2023-02-19 11:19 被阅读0次

    目标

    希望table组件能支持多列的左右固定,像这样


    antd多列固定

    材料

    现在的table有固定的头和一列固定的checkbox,如下

    改造前
    table 元素的样式
    style
    因为table元素的table-layout 并没有设置为fixed,所以列目前是自适应的宽度。

    开始

    固定列的宽度

    在table里,使用<colgroup><col>来设置列的宽度。
    入参需要所有table列的配置的数组,如果有父子列则只需要子列的配置。
    格式大概是

      {
        Header: t('Table.Header.BatchId'),
        id: 'invoiceId',
        fixed: 'left',
        width: 115,
        accessor: NoEmptyAccessor('invoiceId'),
      }
    

    相关入参

    • width 来固定列的宽度
    • fixed:'left'|'right' 来确定列是靠左还是靠右

    生成 <colgroup>

    export function ColGroup<D extends object = {}>({
      allFlatColumns,
      hasSelect,
    }: {
      allFlatColumns: readonly ColumnInstance<D>[];
      hasSelect: boolean;
    }) {
      const children = [];
      if (hasSelect) children.push(<S.InnerCol width={CHECKBOX_WIDTH} />); // when there has checkbox
    
      allFlatColumns.forEach((item) => {
        if (item.isVisible && typeof item.width === 'number') {
          children.push(<S.InnerCol width={item.width} key={item.id} />);
        }
      });
    
      return <colgroup>{children}</colgroup>;
    }
    
    export const InnerCol = styled.col<{ width?: number }>(({ width }) => css`
      &:first-child{
        min-width: ${width || 60}px;
      }
      ${width && css`
        width: ${width}px;
        min-width: ${width}px;
        max-width: ${width}px;
      `}
    `);
    

    使用

    <ColGroup allFlatColumns={allColumns} hasSelect={!!onSelected} />
    

    距离

    规定了列的宽度后,还需要确定距离左右边界的offset,然后得到需要固定的列的Map以便render时候读取,代码如下:

    export enum Fixed {
      left = 'left',
      right = 'right',
    }
    
    interface Sticky {
      fixed: Fixed;
      offset: number;
      shadow?: boolean;
    }
    
    type FixedColumn<D extends object> = ColumnInstance<D> & {
      sticky: Sticky;
    };
    
    const CHECKBOX_WIDTH = 60;
    
    function genSticky(
      fixed: Sticky['fixed'],
      offset: Sticky['offset'],
      shadow?: Sticky['shadow'],
    ) {
      const val: Sticky = { fixed, offset };
      if (shadow) val.shadow = shadow;
      return val;
    }
    
    export const FIXED_LEFT_0 = genSticky(Fixed.left, 0);
    
    export function getTableFixedInfo<D extends object = {}>(
      allFlatColumns: readonly ColumnInstance<D>[],
      hasSelect: boolean,
    ) {
      const fixedLeftColumns: FixedColumn<D>[] = [];
      const fixedRightColumns: FixedColumn<D>[] = [];
      const fixedConfig: Map<string, Sticky> = new Map();
      let offsetLeft = hasSelect ? CHECKBOX_WIDTH : 0;
      let offsetRight = 0;
    
      // initial sticky parameter
      allFlatColumns.forEach((item) => {
        if (item?.fixed === Fixed.left) {
          const sticky: Sticky = genSticky(
            Fixed.left,
            offsetLeft,
          );
          fixedLeftColumns.push({ ...item, sticky });
          fixedConfig.set(item.id, sticky);
          if (typeof item.width === 'number') {
            offsetLeft += item.width;
          }
        } else if (item.fixed === Fixed.right) {
          const sticky: Sticky = genSticky(
            Fixed.right,
            offsetRight,
          );
          fixedRightColumns.push({ ...item, sticky });
          fixedConfig.set(item.id, sticky);
        }
      });
      if (fixedLeftColumns.length) {
        fixedLeftColumns[fixedLeftColumns.length - 1].sticky.shadow = true;
      }
    
      // handle fixedRightColumns sticky parameter
      // eslint-disable-next-line no-plusplus
      for (let idx = fixedRightColumns.length - 1; idx >= 0; idx--) {
        const item = fixedRightColumns[idx];
        item.sticky.offset = offsetRight;
        if (idx === 0) item.sticky.shadow = true;
        if (typeof item?.width === 'number') {
          offsetRight += item.width;
        }
      }
    
      return {
        fixedConfig,
        parentHeaderSticky: genSticky(Fixed.left, hasSelect ? CHECKBOX_WIDTH : 0),
      };
    }
    

    阴影

    阴影就是在列上加一个伪元素,阴影样式如下:

    type Sticky = { offset: number; fixed: 'left' | 'right'; shadow?: boolean };
    
    const VerticalShadow = (position: Sticky['fixed']) => css`
      position: absolute;
      top: 0;
      bottom: -1px;
      width: 30px;
      transition: box-shadow .3s;
      content: "";
      pointer-events: none;
      direction: ltr;
      
      ${position === 'right' && css`
        box-shadow: inset -10px 0 8px -8px rgb(5 5 5 / 6%);
        border-inline-end: 1px solid ${getColorByName('borderDivider')};
        transform: translateX(-100%);
        left: 0;
      `}
    
      ${position === 'left' && css`
        box-shadow: inset 10px 0 8px -8px rgb(5 5 5 / 6%);
        transform: translateX(100%);
        right: 0;
      `}
    `;
    

    使用

     ${sticky.shadow && css`
            &::after {
              ${VerticalShadow(sticky.fixed)}
            }
          `}
    

    效果

    效果图
    动图

    相关文章

      网友评论

          本文标题:多个固定列的React table组件

          本文链接:https://www.haomeiwen.com/subject/eksikdtx.html