44import JSZip from "jszip" ;
55import { TableData } from "../types" ;
66import {
7- defaults ,
87 element ,
98 elementAttributes ,
109 queryTableNotFoundErr ,
1110 queryTableXmlPath ,
1211 sheetsNotFoundErr ,
13- sheetsXmlPath ,
1412 tableNotFoundErr ,
15- tableXmlPath ,
1613 textResultType ,
1714 workbookXmlPath ,
1815 xmlTextResultType ,
@@ -21,18 +18,28 @@ import documentUtils from "./documentUtils";
2118import { v4 } from "uuid" ;
2219import { DOMParser , XMLSerializer } from "xmldom-qsa" ;
2320
24- const updateTableInitialDataIfNeeded = async ( zip : JSZip , tableData ?: TableData , updateQueryTable ?: boolean ) : Promise < void > => {
21+ /**
22+ * Update initial data for a table, its sheet, query table, and defined name if provided.
23+ * @param zip - The JSZip instance containing workbook parts.
24+ * @param cellRangeRef - Cell range reference (e.g. "A1:C5").
25+ * @param sheetPath - Path to the sheet XML within the zip.
26+ * @param tablePath - Path to the table XML within the zip.
27+ * @param tableName - Name of the table.
28+ * @param tableData - Optional TableData containing headers and rows.
29+ * @param updateQueryTable - Whether to update the associated queryTable part.
30+ */
31+ const updateTableInitialDataIfNeeded = async ( zip : JSZip , cellRangeRef : string , sheetPath : string , tablePath : string , sheetName : string , tableData ?: TableData , updateQueryTable ?: boolean ) : Promise < void > => {
2532 if ( ! tableData ) {
2633 return ;
2734 }
2835
29- const sheetsXmlString : string | undefined = await zip . file ( sheetsXmlPath ) ?. async ( textResultType ) ;
36+ const sheetsXmlString : string | undefined = await zip . file ( sheetPath ) ?. async ( textResultType ) ;
3037 if ( sheetsXmlString === undefined ) {
3138 throw new Error ( sheetsNotFoundErr ) ;
3239 }
3340
34- const newSheet : string = updateSheetsInitialData ( sheetsXmlString , tableData ) ;
35- zip . file ( sheetsXmlPath , newSheet ) ;
41+ const newSheet : string = updateSheetsInitialData ( sheetsXmlString , tableData , cellRangeRef ) ;
42+ zip . file ( sheetPath , newSheet ) ;
3643
3744 if ( updateQueryTable ) {
3845 const queryTableXmlString : string | undefined = await zip . file ( queryTableXmlPath ) ?. async ( textResultType ) ;
@@ -49,20 +56,28 @@ const updateTableInitialDataIfNeeded = async (zip: JSZip, tableData?: TableData,
4956 throw new Error ( sheetsNotFoundErr ) ;
5057 }
5158
52- const newWorkbook : string = updateWorkbookInitialData ( workbookXmlString , tableData ) ;
59+ const newWorkbook : string = updateWorkbookInitialData ( workbookXmlString , sheetName + GenerateReferenceFromString ( cellRangeRef ) ) ;
5360 zip . file ( workbookXmlPath , newWorkbook ) ;
5461 }
5562
56- const tableXmlString : string | undefined = await zip . file ( tableXmlPath ) ?. async ( textResultType ) ;
63+ const tableXmlString : string | undefined = await zip . file ( tablePath ) ?. async ( textResultType ) ;
5764 if ( tableXmlString === undefined ) {
5865 throw new Error ( tableNotFoundErr ) ;
5966 }
6067
61- const newTable : string = updateTablesInitialData ( tableXmlString , tableData , updateQueryTable ) ;
62- zip . file ( tableXmlPath , newTable ) ;
68+ const newTable : string = updateTablesInitialData ( tableXmlString , tableData , cellRangeRef , updateQueryTable ) ;
69+ zip . file ( tablePath , newTable ) ;
6370} ;
6471
65- const updateTablesInitialData = ( tableXmlString : string , tableData : TableData , updateQueryTable = false ) : string => {
72+ /**
73+ * Generate updated table XML string with new columns, reference, and filter range.
74+ * @param tableXmlString - Original table XML.
75+ * @param tableData - TableData containing column names.
76+ * @param cellRangeRef - Cell range reference.
77+ * @param updateQueryTable - Whether to include queryTable attributes.
78+ * @returns Serialized XML string of the updated table.
79+ */
80+ const updateTablesInitialData = ( tableXmlString : string , tableData : TableData , cellRangeRef : string , updateQueryTable = false ) : string => {
6681 const parser : DOMParser = new DOMParser ( ) ;
6782 const serializer : XMLSerializer = new XMLSerializer ( ) ;
6883 const tableDoc : Document = parser . parseFromString ( tableXmlString , xmlTextResultType ) ;
@@ -84,21 +99,26 @@ const updateTablesInitialData = (tableXmlString: string, tableData: TableData, u
8499 tableColumns . setAttribute ( elementAttributes . count , tableData . columnNames . length . toString ( ) ) ;
85100 tableDoc
86101 . getElementsByTagName ( element . table ) [ 0 ]
87- . setAttribute ( elementAttributes . reference , `A1: ${ documentUtils . getCellReferenceRelative ( tableData . columnNames . length - 1 , tableData . rows . length + 1 ) } ` ) ;
102+ . setAttribute ( elementAttributes . reference , cellRangeRef ) ;
88103 tableDoc
89104 . getElementsByTagName ( element . autoFilter ) [ 0 ]
90- . setAttribute ( elementAttributes . reference , `A1: ${ documentUtils . getCellReferenceRelative ( tableData . columnNames . length - 1 , tableData . rows . length + 1 ) } ` ) ;
105+ . setAttribute ( elementAttributes . reference , cellRangeRef ) ;
91106
92107 return serializer . serializeToString ( tableDoc ) ;
93108} ;
94109
95- const updateWorkbookInitialData = ( workbookXmlString : string , tableData : TableData ) : string => {
110+ /**
111+ * Update the definedName element in workbook XML to a custom name.
112+ * @param workbookXmlString - Original workbook XML string.
113+ * @param customDefinedName - New defined name text content (e.g. "!$A$1:$C$5").
114+ * @returns Serialized XML string of the updated workbook.
115+ */
116+ const updateWorkbookInitialData = ( workbookXmlString : string , customDefinedName : string ) : string => {
96117 const newParser : DOMParser = new DOMParser ( ) ;
97118 const newSerializer : XMLSerializer = new XMLSerializer ( ) ;
98119 const workbookDoc : Document = newParser . parseFromString ( workbookXmlString , xmlTextResultType ) ;
99120 const definedName : Element = workbookDoc . getElementsByTagName ( element . definedName ) [ 0 ] ;
100- definedName . textContent =
101- defaults . sheetName + `!$A$1:${ documentUtils . getCellReferenceAbsolute ( tableData . columnNames . length - 1 , tableData . rows . length + 1 ) } ` ;
121+ definedName . textContent = customDefinedName ;
102122
103123 return newSerializer . serializeToString ( workbookDoc ) ;
104124} ;
@@ -122,44 +142,69 @@ const updateQueryTablesInitialData = (queryTableXmlString: string, tableData: Ta
122142 return serializer . serializeToString ( queryTableDoc ) ;
123143} ;
124144
125- const updateSheetsInitialData = ( sheetsXmlString : string , tableData : TableData ) : string => {
145+ /**
146+ * Update sheet XML with header row and data rows based on TableData.
147+ * @param sheetsXmlString - Original sheet XML string.
148+ * @param tableData - TableData containing headers and rows.
149+ * @param cellRangeRef - Cell range reference.
150+ * @returns Serialized XML string of the updated sheet.
151+ */
152+ const updateSheetsInitialData = ( sheetsXmlString : string , tableData : TableData , cellRangeRef : string ) : string => {
153+ let { row, column } = documentUtils . GetStartPosition ( cellRangeRef ) ;
126154 const parser : DOMParser = new DOMParser ( ) ;
127155 const serializer : XMLSerializer = new XMLSerializer ( ) ;
128156 const sheetsDoc : Document = parser . parseFromString ( sheetsXmlString , xmlTextResultType ) ;
129157 const sheetData : Element = sheetsDoc . getElementsByTagName ( element . sheetData ) [ 0 ] ;
130158 sheetData . textContent = "" ;
131- let rowIndex = 0 ;
159+
132160 const columnRow : Element = sheetsDoc . createElementNS ( sheetsDoc . documentElement . namespaceURI , element . row ) ;
133- columnRow . setAttribute ( elementAttributes . row , ( rowIndex + 1 ) . toString ( ) ) ;
134- columnRow . setAttribute ( elementAttributes . spans , "1 :" + tableData . columnNames . length ) ;
161+ columnRow . setAttribute ( elementAttributes . row , row . toString ( ) ) ;
162+ columnRow . setAttribute ( elementAttributes . spans , column + " :" + ( column + tableData . columnNames . length - 1 ) ) ;
135163 columnRow . setAttribute ( elementAttributes . x14acDyDescent , "0.3" ) ;
136164 tableData . columnNames . forEach ( ( col : string , colIndex : number ) => {
137- columnRow . appendChild ( documentUtils . createCell ( sheetsDoc , colIndex , rowIndex , col ) ) ;
165+ columnRow . appendChild ( documentUtils . createCell ( sheetsDoc , colIndex + column - 1 , row - 1 , col ) ) ;
138166 } ) ;
139167 sheetData . appendChild ( columnRow ) ;
140- rowIndex ++ ;
141- tableData . rows . forEach ( ( row ) => {
168+ row ++ ;
169+
170+ tableData . rows . forEach ( ( _row ) => {
142171 const newRow = sheetsDoc . createElementNS ( sheetsDoc . documentElement . namespaceURI , element . row ) ;
143- newRow . setAttribute ( elementAttributes . row , ( rowIndex + 1 ) . toString ( ) ) ;
144- newRow . setAttribute ( elementAttributes . spans , "1 :" + row . length ) ;
172+ newRow . setAttribute ( elementAttributes . row , row . toString ( ) ) ;
173+ newRow . setAttribute ( elementAttributes . spans , column + " :" + ( column + tableData . columnNames . length - 1 ) ) ;
145174 newRow . setAttribute ( elementAttributes . x14acDyDescent , "0.3" ) ;
146- row . forEach ( ( cellContent , colIndex ) => {
147- newRow . appendChild ( documentUtils . createCell ( sheetsDoc , colIndex , rowIndex , cellContent ) ) ;
175+ _row . forEach ( ( cellContent , colIndex ) => {
176+ newRow . appendChild ( documentUtils . createCell ( sheetsDoc , colIndex + column - 1 , row - 1 , cellContent ) ) ;
148177 } ) ;
149178 sheetData . appendChild ( newRow ) ;
150- rowIndex ++ ;
179+ row ++ ;
151180 } ) ;
152- const reference = documentUtils . getTableReference ( tableData . rows [ 0 ] . length - 1 , tableData . rows . length + 1 ) ;
153181
154- sheetsDoc . getElementsByTagName ( element . dimension ) [ 0 ] . setAttribute ( elementAttributes . reference , reference ) ;
155- sheetsDoc . getElementsByTagName ( element . selection ) [ 0 ] . setAttribute ( elementAttributes . sqref , reference ) ;
182+ sheetsDoc . getElementsByTagName ( element . dimension ) [ 0 ] . setAttribute ( elementAttributes . reference , cellRangeRef ) ;
183+ sheetsDoc . getElementsByTagName ( element . selection ) [ 0 ] . setAttribute ( elementAttributes . sqref , cellRangeRef ) ;
156184 return serializer . serializeToString ( sheetsDoc ) ;
157185} ;
158186
187+ /**
188+ * Add Excel-style dollar signs and a '!' prefix to a cell range.
189+ * Converts "A1:B2" into "!$A$1:$B$2".
190+ * @param cellRangeRef - Range reference string without dollar signs.
191+ * @returns Range with dollar signs and prefix.
192+ */
193+ const GenerateReferenceFromString = ( cellRangeRef : string ) : string => {
194+ return "!" + cellRangeRef . split ( ":" ) . map ( part => {
195+ const match = part . match ( / ^ ( [ A - Z a - z ] + ) ( \d + ) $ / ) ;
196+ if ( match ) {
197+ const [ , col , row ] = match ;
198+ return `$${ col . toUpperCase ( ) } $${ row } ` ;
199+ }
200+ } ) . join ( ":" ) ;
201+ }
202+
159203export default {
160204 updateTableInitialDataIfNeeded,
161205 updateSheetsInitialData,
162206 updateWorkbookInitialData,
163207 updateTablesInitialData,
164208 updateQueryTablesInitialData,
209+ GenerateReferenceFromString,
165210} ;
0 commit comments