@@ -93,6 +93,137 @@ export const useXLSXImport = ({
9393 //@ts -expect-error, later
9494 await workbook . xlsx . load ( arrayBuffer ) ;
9595 const worksheet = workbook . getWorksheet ( 1 ) ;
96+ // Extract hyperlinks, freeze info, and cell formatting from all worksheets
97+ const hyperlinksBySheet : Record <
98+ number ,
99+ Record < string , { linkType : string ; linkAddress : string } >
100+ > = { } ;
101+ const frozenBySheet : Record <
102+ number ,
103+ {
104+ type :
105+ | 'row'
106+ | 'column'
107+ | 'both'
108+ | 'rangeRow'
109+ | 'rangeColumn'
110+ | 'rangeBoth' ;
111+ range : { row_focus : number ; column_focus : number } ;
112+ }
113+ > = { } ;
114+ const cellStylesBySheet : Record <
115+ number ,
116+ Record <
117+ string ,
118+ {
119+ bl ?: number ;
120+ it ?: number ;
121+ fs ?: number ;
122+ ff ?: string ;
123+ fc ?: string ;
124+ bg ?: string ;
125+ un ?: number ;
126+ }
127+ >
128+ > = { } ;
129+ workbook . eachSheet ( ( ws , sheetIndex ) => {
130+ const idx = sheetIndex - 1 ; // exceljs is 1-based
131+
132+ // Hyperlinks
133+ const sheetHyperlinks : Record <
134+ string ,
135+ { linkType : string ; linkAddress : string }
136+ > = { } ;
137+
138+ // Freeze panes from worksheet views
139+ const views = ws . views ;
140+ if ( views && views . length > 0 ) {
141+ const view = views [ 0 ] ;
142+ if ( view . state === 'frozen' ) {
143+ const xSplit = view . xSplit || 0 ;
144+ const ySplit = view . ySplit || 0 ;
145+ let type : 'rangeRow' | 'rangeColumn' | 'rangeBoth' | null = null ;
146+ if ( xSplit > 0 && ySplit > 0 ) type = 'rangeBoth' ;
147+ else if ( ySplit > 0 ) type = 'rangeRow' ;
148+ else if ( xSplit > 0 ) type = 'rangeColumn' ;
149+ if ( type ) {
150+ frozenBySheet [ idx ] = {
151+ type,
152+ range : {
153+ row_focus : ySplit - 1 ,
154+ column_focus : xSplit - 1 ,
155+ } ,
156+ } ;
157+ }
158+ }
159+ }
160+
161+ // Cell-level formatting and hyperlinks
162+ const styles : Record <
163+ string ,
164+ {
165+ bl ?: number ;
166+ it ?: number ;
167+ fs ?: number ;
168+ ff ?: string ;
169+ fc ?: string ;
170+ bg ?: string ;
171+ un ?: number ;
172+ }
173+ > = { } ;
174+ ws . eachRow ( { includeEmpty : false } , ( row , rowNumber ) => {
175+ row . eachCell ( { includeEmpty : false } , ( cell , colNumber ) => {
176+ const key = `${ rowNumber - 1 } _${ colNumber - 1 } ` ;
177+
178+ if ( cell . hyperlink ) {
179+ sheetHyperlinks [ key ] = {
180+ linkType : 'webpage' ,
181+ linkAddress : cell . hyperlink ,
182+ } ;
183+ }
184+
185+ // Extract cell formatting
186+ const font = cell . style ?. font ;
187+ const fill = cell . style ?. fill ;
188+ const cellStyle : Record < string , string | number > = { } ;
189+
190+ if ( font ) {
191+ if ( font . bold ) cellStyle . bl = 1 ;
192+ if ( font . italic ) cellStyle . it = 1 ;
193+ if ( font . underline ) cellStyle . un = 1 ;
194+ if ( font . size ) cellStyle . fs = font . size ;
195+ if ( font . name ) cellStyle . ff = font . name ;
196+ if ( font . color ?. argb ) {
197+ const argb = font . color . argb ;
198+ const hex = '#' + argb . substring ( argb . length - 6 ) ;
199+ cellStyle . fc = hex ;
200+ }
201+ }
202+ if (
203+ fill ?. type === 'pattern' &&
204+ fill . pattern === 'solid' &&
205+ fill . fgColor
206+ ) {
207+ if ( fill . fgColor . argb ) {
208+ const argb = fill . fgColor . argb ;
209+ const hex = '#' + argb . substring ( argb . length - 6 ) ;
210+ cellStyle . bg = hex ;
211+ }
212+ }
213+ if ( Object . keys ( cellStyle ) . length > 0 ) {
214+ styles [ key ] = cellStyle ;
215+ }
216+ } ) ;
217+ } ) ;
218+
219+ if ( Object . keys ( sheetHyperlinks ) . length > 0 ) {
220+ hyperlinksBySheet [ idx ] = sheetHyperlinks ;
221+ }
222+ if ( Object . keys ( styles ) . length > 0 ) {
223+ cellStylesBySheet [ idx ] = styles ;
224+ }
225+ } ) ;
226+
96227 dropdownInfo =
97228 worksheet
98229 ?. getSheetValues ( )
@@ -154,7 +285,7 @@ export const useXLSXImport = ({
154285 }
155286 const sheetArray = ydocRef . current . getArray ( dsheetId ) ;
156287 const localSheetsArray = Array . from ( sheetArray ) as Sheet [ ] ;
157- sheets = sheets . map ( ( sheet ) => {
288+ sheets = sheets . map ( ( sheet , sheetIndex ) => {
158289 const lastCell = sheet ?. celldata ?. [ sheet . celldata . length - 1 ] ;
159290
160291 const lastRow = lastCell ?. r ?? 0 ;
@@ -164,31 +295,63 @@ export const useXLSXImport = ({
164295 sheet . column = Math . max ( lastCol , 36 ) ;
165296
166297 if ( ! sheet . id ) {
167- sheet . id = sheetEditorRef . current ?. getSettings ( ) . generateSheetId ( ) ;
298+ sheet . id = sheetEditorRef . current
299+ ?. getSettings ( )
300+ . generateSheetId ( ) ;
168301 }
169302 if ( sheet . calcChain ) {
170303 sheet . calcChain = sheet . calcChain . map ( ( chain ) => {
171- delete chain . id
172- delete chain . index
173- chain . id = sheet . id
174- return chain
175- } )
304+ delete chain . id ;
305+ delete chain . index ;
306+ chain . id = sheet . id ;
307+ return chain ;
308+ } ) ;
309+ }
310+ // Attach freeze pane info
311+ if ( frozenBySheet [ sheetIndex ] ) {
312+ sheet . frozen = frozenBySheet [ sheetIndex ] ;
313+ }
314+ // Attach hyperlinks extracted from exceljs for this sheet
315+ if ( hyperlinksBySheet [ sheetIndex ] ) {
316+ sheet . hyperlink = {
317+ ...( sheet . hyperlink || { } ) ,
318+ ...hyperlinksBySheet [ sheetIndex ] ,
319+ } ;
176320 }
177- return sheet
178- } )
321+ // Apply cell formatting from exceljs (hyperlink styling, bold, italic, bg, etc.)
322+ const hlKeys = hyperlinksBySheet [ sheetIndex ] ;
323+ const styleKeys = cellStylesBySheet [ sheetIndex ] ;
324+ if ( ( hlKeys || styleKeys ) && sheet . celldata ) {
325+ for ( const cell of sheet . celldata ) {
326+ const key = `${ cell . r } _${ cell . c } ` ;
327+ if ( cell . v ) {
328+ // Apply formatting extracted from exceljs
329+ if ( styleKeys ?. [ key ] ) {
330+ Object . assign ( cell . v , styleKeys [ key ] ) ;
331+ }
332+ // Override font color + underline for hyperlink cells
333+ if ( hlKeys ?. [ key ] ) {
334+ cell . v . fc = 'rgb(0, 0, 255)' ;
335+ cell . v . un = 1 ;
336+ }
337+ }
338+ }
339+ }
340+ return sheet ;
341+ } ) ;
179342
180343 let combinedSheets ;
181344
182345 if ( importType === 'merge-current-dsheet' ) {
183- combinedSheets = [ ...localSheetsArray , ...sheets ]
346+ combinedSheets = [ ...localSheetsArray , ...sheets ] ;
184347 } else {
185- combinedSheets = [ ...sheets ]
348+ combinedSheets = [ ...sheets ] ;
186349 }
187350
188351 combinedSheets = combinedSheets . map ( ( sheet , index ) => {
189352 sheet . order = index ;
190- return sheet
191- } )
353+ return sheet ;
354+ } ) ;
192355
193356 setSheetData ( combinedSheets ) ;
194357 ydocRef . current . transact ( ( ) => {
0 commit comments