テーブルをクリップボードにコピーしてExcelに「正しく」ペーストする
概要
ReactにはさまざまなData Grid Component[1]があります。
一方、それらの大多数はExcelにうまくコピーできないという問題があります。
それを解決するために、いろいろ試してみたのでその備忘録を記します。
検証環境
- macOS
- Excel for Mac
- Microsoft 365
- ver. 16.96.1
結論
TSV形式だとうまくいかない(少なくとも、Mac環境では)。
HTML(Blob)形式でコピーするべし。
検証
最小環境を作る
特に工夫も何もないデータ構造です。
TSV形式でコピーする
walkframe/gridsheetの実装を参考にしました。
上記のような感じで、テーブルをTSVに変換して、クリップボードに書き込んでいます。
これを、先ほどの最小環境に付け足してみます。
実際に動かして、Consoleに吐き出させると以下のようになります。
TSVに変換できていますね。
これを、Excelに貼り付けてみるとどうなるでしょうか。。?
あ、、、\t
が失われてしまいました。。。
一部情報では、UNIX系の\n
ではうまくいかない。という情報があったので、\r\n
でも試しましたが、やはり同じようです🧐🧐🧐
HTML形式でコピーする
実はこっちが本命です。
HTMLをBlob形式にして、クリップボードに書き込むというものです。
参考:copy html table into clipboard with format
コアはこれです。
const tableCopy = (table: string[][]) => {
let html = "";
html += "<table>";
table.forEach((row) => {
html += "<tr>";
row.forEach((value) => {
html += `<td>${value}</td>`;
});
html += "</tr>";
});
html += "</table>";
console.log(html);
const htmlBlob = new Blob([html], { type: "text/html" });
navigator.clipboard.write([
new ClipboardItem({
"text/html": htmlBlob,
}),
]);
};
これを、Excelに貼り付けてみるとどうなるでしょうか。。?
うまくいきました!
ただ、TSVという扱い易い形式ではなくBlobに変換してしまっているので、用途等は見極めてご使用ください。
Spreadsheetではどうやってる?
ということで、うまく行ってるSpreadsheetの実装をのぞいてみましょう。
ということで、Clipboardの中を覗くツールを作ってみます。
import "./App.css";
function App() {
return (
<>
<button
onClick={async () => {
const res = await navigator.clipboard.read();
res.forEach((val) => {
val.types.forEach((type) => {
val.getType(type)
.then((contents) => {
return contents.text();
})
.then((text) => {
console.log(type, text);
});
});
});
}}
>
クリップボードを覗く
</button>
</>
);
}
export default App;
Spreadsheetからデータをコピーしてみましょう。
すると以下のようなデータが格納されていることがわかりました。
<google-sheets-html-origin>
<style type="text/css"><!--td {border: 1px solid #cccccc;}br {mso-data-placement:same-cell;}--></style>
<table xmlns="http://www.w3.org/1999/xhtml" cellspacing="0" cellpadding="0" dir="ltr" border="1" style="table-layout:fixed;font-size:10pt;font-family:Arial;width:0px;border-collapse:collapse;border:none" data-sheets-root="1" data-sheets-baot="1">
<colgroup>
<col width="100"/>
<col width="100"/>
<col width="100"/>
</colgroup>
<tbody>
<tr style="height:21px;">
<td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;">id</td>
<td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;">name</td>
<td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;">email</td>
</tr>
<tr style="height:21px;">
<td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;text-align:right;">1</td>
<td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;">Bob</td>
<td style="border-right:1px solid transparent;overflow:visible;padding:2px 0px 2px 0px;vertical-align:bottom;">
<div style="white-space:nowrap;overflow:hidden;position:relative;width:196px;left:3px;">
<div style="float:left;">
bob@example.com
</div>
</div>
</td>
</tr>
<!-- 略 -->
</tbody>
</table>
id name email
1 Bob bob@example.com
2 Alex alex@example.com
3 Ana ana@example.com
TSVとHTMLどっちも格納してるんですね
-
本記事ではData Grid Componentという用語を、Google Spreadsheetの表部分のような、Excelのように選択, コピー, ペースト, 編集できるもの。として使っています。 ↩︎
Discussion