mrlmx / blogs

limingxin's blog

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

如何在 antd Table 的选择框中添加 Tooltip?

mrlmx opened this issue · comments

Tips,如果你着急想要解决方案:


下面是实现这个功能的心路历程,个人觉得比较有意思,所以记录下来 😜。

前段时间,产品提了一个需求:想要给那些 Table 里面禁止选中的行中加上 Tooltip,提示为什么不允许选中,提升用户体验,就像下面这样 👇🏻:

image

我们知道 antd 的 Table 组件可以通过 rowSelection 属性,控制表格行是否可选择,所以我就直接去 rowSelection 这部分文档里面找相关的配置,可是上下 简单浏览 一番,发现并没有这方面的内容。

所以又直奔 issue,搜索关键字:rowSelection tooltip ,找到了下面几个相关的问题:

找到的唯一可行性方案是:自定义 Column

It's not a common requirement. You can customize the column by your own: https://codesandbox.io/s/1q2x7jx9nj

看这架势,意思是需要咱们自己手动实现一套完整的选择逻辑啊。这让时间本不宽裕的我们,更是雪上加霜 😫。

所以,我随即得出了结论:

这个需求暂时做不了。🤥

可是,这确实是个有意义需求啊 ~ 😭,既然是正经需求,要不,咱们再想想办法 🤔 ?

Hack 思路

因为偶尔会看 antd 的代码,所以本地保存的有他的代码,打开代码的第一步就是 pull 一下最新代码,antd 每周发一版,是在太厉害了(小声BB 🤫)

PS:以下代码的版本均为:antd@4.20.0

再次经过一番 简单浏览 之后,找到了 渲染 CheckBox 的代码

// Record checked
return {
  node: (
    <Checkbox
      {...checkboxProps}
      indeterminate={mergedIndeterminate}
      checked={checked}
      skipGroup
      onClick={(e) => e.stopPropagation()}
      onChange={({ nativeEvent }) => {
        // 省略此部分源码内容
      }}
      />
  ),
  checked,
};

可以看到:{...checkboxProps} 这里是通过解构,直接把 checkboxProps 传给了 CheckBox 组件。

那是不是说,我可以在 checkboxProps 中添加任何 CheckBox 组件支持的属性呢 😏?

然后我在 getCheckboxProps 中返回了如下信息:

<Table
  rowSelection={{
    getCheckboxProps: (record) => {
      return {
        disabled: true,
        children: <span style={{ color: "red" }}>Test</span>,
      };
    },
  }}
  columns={columns}
  dataSource={data}
/>;

嘿嘿嘿,他出现了 😜。

image-20220426200913014.png

验证猜想之后,把代码进行如下改造:

  • 新增一个 span,设置为 CheckBox 的高宽
  • span 外面包上 Tooltip 组件
  • 通过 absolutelyspan 绝对定位到 CheckBox 的位置
  • 设置 z-index,将 span 放在 CheckBox 上方
<Table
  rowSelection={{
    getCheckboxProps: (record) => {
      const props = {};
      if (record.age < 18) {
        props.disabled = true;
        props.children = (
          <Tooltip title="小于 18 岁">
            <span
              style={{
                width: 16,
                height: 16,
                position: "absolute",
                top: 19,
                left: 8,
                zIndex: 1,
                border: "1px solid red",
              }}
            />
          </Tooltip>
        );
      }
      return props;
    },
  }}
  columns={columns}
  dataSource={data}
/>;

image-20220426210534716.png

但是还有些瑕疵:展示 Tooltip 的 CheckBox 纵向错位了,通过审查元素发现,是因为我们加了 children,把 CheckBox 给挤到旁边了。

image-20220429220100896.png

既然被挤走了,那把容器宽度缩小一点 🤔,有一个简单的方法是直接通过 css 覆盖修改这一列的宽度。

  • rowSelection 有一个控制选择框宽度的属性:columnWidth,但就算设置的很小,整列还是会被内容撑开
  • 下面的代码仅用来做演示,没有没有加前缀,注意不要把全局的 Table 列宽都修改了。
.ant-table-selection-column > .ant-checkbox-wrapper{
  width: 16px;
}

.ant-table-selection-column > .ant-radio-wrapper{
  width: 16px;
}

image-20220505193226535.png

好了,到了这一步,Table 的 CheckBox 上添加 Tooltip 已经完成了,Radio 思路也一致,只需要把 span 的坐标调整一下,然后加上圆角即可,完整代码可以见本文开头的链接。

发现原生支持

需求顺利上线之后,想着写篇文章记录一下解决方案,贴到上面几个 issue 中,这样可以在官方支持之前,让大家先临时实现这个功能。

所以想顺带看一下 antd Table 中选择列的渲染逻辑,避免有其他被我忽略的细节导致 Bug。没想到这一看,就把我看懵了,我发现了 renderSelectionCell 这个函数 😱。

const renderSelectionCell = (_: any, record: RecordType, index: number) => {
  const { node, checked } = renderCell(_, record, index);

  if (customizeRenderCell) {
    return customizeRenderCell(checked, record, index, node);
  }

  return node;
};

竟然有个调用 customizeRenderCell 函数的逻辑,一看这函数名,我就觉得有点不对劲🤔,而且还把相关的参数都传了进去。

通过一番顺藤摸瓜,发现了根源。

首先是从 rowSelection 中获取 renderCell 参数,并且重命名为 customizeRenderCell

export default function useSelection<RecordType>(
rowSelection: TableRowSelection<RecordType> | undefined,
 config: UseSelectionConfig<RecordType>
): [TransformColumns<RecordType>, Set<Key>] {
  const {
    // 省略此部分源码内容
    ...,
    renderCell: customizeRenderCell,
    // 省略此部分源码内容
    ...
  } = rowSelection || {};
}

然后在 Table 中找到了调用 useSelection代码片段

const [transformSelectionColumns, selectedKeySet] = useSelection<RecordType>(rowSelection, {
  prefixCls,
  data: mergedData,
  pageData,
  getRowKey,
  getRecordByKey,
  expandType,
  childrenColumnName,
  locale: tableLocale,
  getPopupContainer,
}
);

useSelection 的第一个参数 rowSelection,就是我们在 Table 中传入的 rowSelection 属性 😨。

所以,文档中的 rowSelection 的这个 renderCell 属性竟然被我完整忽略了 😰。。。

image-20220505201405914.png

好了,既然发现了这个秘密,那咱们就试一下效果吧 🙄。

<Table
  rowSelection={{
    renderCell(checked, record, index, node) {
      if (record.age < 18) {
        return <Tooltip title="未满 18 岁">{node}</Tooltip>;
      }
      return node;
    },
      getCheckboxProps: (record) => {
        let checkboxProps = {};
        if (record.age < 18) {
          checkboxProps.disabled = true;
        }
        return checkboxProps;
      },
  }}
  columns={columns}
  dataSource={data}
/>;

不得不说,简直完美 🤗。

image-20220505204447544.png

修改官方文档

鉴于在写这个需求之前,也问过其他同事,有没有做过在 Table 的可选列中展示 Tooltip 的需求,他们给我的答复都是官方文档中没有相关配置,暂时不支持这个功能,就把这类需求都拒绝了。

所以我感觉应该有很大一部分小伙伴会忽略掉 rowSelection 的这个 renderCell 属性,既然如此,就在官方文档中把这个场景提一下,可以让其他小伙伴少走些弯路,早点下班 😜。

写好相关示例并完善文档之后,就给官方提了一个 PR,又是美好的一天 😄。