背景
卑微的Antv/S2客服:“喂,你好,这里是Antv/S2金牌服务专线,请问有什么可以帮你?”
小李: “救命啊!!电子表格想要默认好用快速上手就选Antv/S2,试问谁不知道?🤔”
卑微的Antv/S2 客服:“雀食我们也知道,那你遇到什么问题了呢”
小李: “这次我们就选了Antv/S2,做我们的数据预览表格呢,⚡ 啪的一下 ⚡,很快啊,一首歌的时间就完成需求下班了”
卑微的Antv/S2 客服:“听起来针不戳,那么问题究竟是什么呢 ̄□ ̄||?”
小李: “这需求做完当天就上线了,但老板用了直接暴跳如雷,说要我结清工资走人(略带哭腔),我一问你知道是啥,从Antv/S2里面复制出来的部分根本就没办法粘贴到语雀文档呐,全变成字符串了”
卑微的Antv/S2 客服:“让我试试...咦...这个还真不行...可是为什么别人竞品都支持呢...”
小李:(卒)
场景回放
当我们在谈论「复制」时,我们在谈论什么?
Antv/S2为我们贴心的内置了复制功能,只需在s2Options
加入如下代码:
const options: S2Options = {
interaction: {
enableCopy: true,
},
};
即可随时使用ctrl+c
触发复制选中单元格中的代码。
而在Antv/S2代码内部,会触发getSelectedData
方法,并会根据表格所属种类(透视表、明细表)将相应数据转换成二维矩阵(string[][]),随后processCopyData将其序列化成text/plain
型文本,并调用``navigator.clipboard.writeText`浏览器API将其写入到剪贴板。
可是为什么竞品复制出来的内容不一样呢?明明大家用的都是一样的navigator.clipboard.writeText
等等!这里不是还有一个.write()
API吗?看起来似乎能解决我们的问题。
逻辑对比😑
Antv/S2的逻辑
先看看S2复制出来的内容
嗯,一个标准的ClipboardItem,里面
好,很有精神, 使用了标准的制表符\t
来做同列数据的分割、\r\n
标准的换行符来做数据分行。值得一提的是,这也是业界标准的表格逻辑。正因如此,复制到excel,语雀数据表,google sheets等等电子表格产品都是完全没有问题的
再看看我们的神秘竞品:
神秘竞品
好家伙,终于发现了华点, 竞品里clipboardItem居然有两个MIMEType
- text/html
- text/plain
上文中使用到的代码:
const clipboardItems = await navigator.clipboard.read()
const textItem = await clipboardItems[0].getType('text/plain')
const htmlItem = await clipboardItems[0].getType('text/html')
const text = await textItem.text()
const html = await htmlItem.text()
疑问解答👊
疑惑都得到了解答。在复制的时候,仅用一次操作,我们可以在剪贴板内写入各种不同MIMEType的数据(比如text/html
, img/png
等等。而读取剪贴板的一方,可以自由选择最适合其使用场景的格式。与之相对的,我们也可以使用无格式复制
来强制使用纯文本来复制内容。
下面来看看从vscode中复制代码到google docs的例子:
对于Antv/S2来说,答案也呼之欲出了,我们只需要在复制的时候为剪贴板提供一份正确的text/html
数据,那在word、语雀文档等产品中就能享受到一键生成表格的感觉🌶
亡羊补牢💪
幸亏Antv/S2架构设计扩展性好,我们仅需实现从数据矩阵到 html 格式 string 的 formatter 即可完成 html 模式复制的支持:
// 把 string[][] 矩阵转换成 CopyableItem
const matrixPlainTextTransformer: MatrixTransformer = (dataMatrix) => {
return {
type: CopyMIMEType.PLAIN,
content: map(dataMatrix, (line) => line.join(newTab)).join(newLine),
};
};
// 把 string[][] 矩阵转换成 CopyableItem
const matrixHtmlTransformer: MatrixTransformer = (dataMatrix) => {
function createTableData(data: string[], tagName: string) {
return data
.map((cell) => `<${tagName}>${escape(cell)}</${tagName}>`)
.join('');
}
function createBody(data: string[][], tagName: string) {
return data
.map((row) => `<${tagName}>${createTableData(row, 'td')}</${tagName}>`)
.join('');
}
return {
type: CopyMIMEType.HTML,
content: `<meta charset="utf-8"><table><tbody>${createBody(
dataMatrix,
'tr',
)}</tbody></table>`,
};
};
支持前:
支持后:
结语
AntV/S2 是多维交叉分析领域的表格解决方案。如果看完这篇文章你有所收获,欢迎给我们的仓库 Star⭐️ 鼓励。
S2 的相关链接:
-
核心层: @antv/s2
-
组件层: @antv/s2-react