//native
/**
* Insert Table
* Insert a table into a Google Doc and fill it from `rows`, an array of rows of cell values, e.g. `[["Name","Role"],["Ada","Engineer"]]` (use empty strings for an empty table). The column count is the longest row. Leave `index` empty to append at the end; `bold_header` bolds the first row. Set `tab_id` (from List Tabs) to target a tab other than the first. Returns the table's start index.
*/
export async function main(
auth: RT.Gdocs,
document_id: string,
rows: string[][],
index: number | undefined,
bold_header: boolean = false,
tab_id: string | undefined
) {
const tabId = tab_id || undefined
const columns = Math.max(...rows.map((r) => r.length))
if (!rows.length || !columns) throw new Error("rows must not be empty")
await batchUpdate(auth, document_id, [
{
insertTable: {
rows: rows.length,
columns,
...(index == null
? { endOfSegmentLocation: { tabId } }
: { location: { index, tabId } }),
},
},
])
// The insert reply has no location: the new table is the last one, or the first one starting after `index`.
const tables = (await getBody(auth, document_id, tabId)).filter(
(el: any) => el.table
)
const table =
index == null
? tables.at(-1)
: tables.find((t: any) => t.startIndex >= index)
// Fill cells from last to first so earlier cells' indexes stay valid.
const requests: any[] = []
table.table.tableRows.forEach((row: any, r: number) =>
row.tableCells.forEach((cell: any, c: number) => {
const text = String(rows[r]?.[c] ?? "")
if (!text) return
const start = cell.content[0].startIndex
requests.unshift({ insertText: { location: { index: start, tabId }, text } })
if (bold_header && r === 0)
requests.splice(1, 0, {
updateTextStyle: {
range: { startIndex: start, endIndex: start + text.length, tabId },
textStyle: { bold: true },
fields: "bold",
},
})
})
)
if (requests.length) await batchUpdate(auth, document_id, requests)
return {
document_id,
table_start_index: table.startIndex,
rows: rows.length,
columns,
}
}
async function batchUpdate(auth: RT.Gdocs, document_id: string, requests: any[]) {
const response = await fetch(
`https://docs.googleapis.com/v1/documents/${document_id}:batchUpdate`,
{
method: "POST",
headers: {
Authorization: `Bearer ${auth.token}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ requests }),
}
)
if (!response.ok) {
throw new Error(`${response.status} ${await response.text()}`)
}
return await response.json()
}
async function getBody(auth: RT.Gdocs, document_id: string, tab_id?: string) {
const url = new URL(`https://docs.googleapis.com/v1/documents/${document_id}`)
if (tab_id) url.searchParams.append("includeTabsContent", "true")
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${auth.token}`,
Accept: "application/json",
},
})
if (!response.ok) {
throw new Error(`${response.status} ${await response.text()}`)
}
const doc = await response.json()
if (!tab_id) return doc.body.content
const find = (tabs: any[]): any =>
tabs.map((t) => (t.tabProperties.tabId === tab_id ? t : find(t.childTabs ?? []))).find(Boolean)
return find(doc.tabs).documentTab.body.content
}
Submitted by hugo989 6 hours ago