Edits history of script submission #23318 for ' Insert Table (gdocs)'

  • bunnative
    One script reply has been approved by the moderators
    Ap­pro­ved
    //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