@@ -4,14 +4,26 @@ import { ListValue, ObjectItem, ValueStatus } from "mendix";
44import { createNanoEvents , Emitter , Unsubscribe } from "nanoevents" ;
55import { ColumnsType , ShowContentAsEnum } from "../../../typings/DatagridProps" ;
66
7- type RowData = Array < string | number | boolean > ;
7+ /** Represents a single Excel cell (SheetJS compatible, simplified) */
8+ interface ExcelCell {
9+ /** Cell type: 's' = string, 'n' = number, 'b' = boolean, 'd' = date */
10+ t : "s" | "n" | "b" | "d" ;
11+ /** Underlying value */
12+ v : string | number | boolean | Date ;
13+ /** Optional Excel number/date format, e.g. "yyyy-mm-dd" or "$0.00" */
14+ z ?: string ;
15+ /** Optional pre-formatted display text */
16+ w ?: string ;
17+ }
18+
19+ type RowData = ExcelCell [ ] ;
820
921type HeaderDefinition = {
1022 name : string ;
1123 type : string ;
1224} ;
1325
14- type ValueReader = ( item : ObjectItem , props : ColumnsType ) => string | boolean | number ;
26+ type ValueReader = ( item : ObjectItem , props : ColumnsType ) => ExcelCell ;
1527
1628type ReadersByType = Record < ShowContentAsEnum , ValueReader > ;
1729
@@ -253,48 +265,81 @@ export class DSExportRequest {
253265const readers : ReadersByType = {
254266 attribute ( item , props ) {
255267 if ( props . attribute === undefined ) {
256- return "" ;
268+ return makeEmptyCell ( ) ;
257269 }
258270
259271 const data = props . attribute . get ( item ) ;
260272
261273 if ( data . status !== "available" ) {
262- return "" ;
274+ return makeEmptyCell ( ) ;
275+ }
276+
277+ const value = data . value ;
278+
279+ if ( value instanceof Date ) {
280+ return {
281+ t : "d" , // date cell
282+ v : value ,
283+ z : "dd/mm/yyyy hh:mm" , // Excel date format
284+ w : value . toISOString ( ) . split ( "T" ) [ 0 ] // human-readable fallback
285+ } ;
263286 }
264287
265- if ( typeof data . value === "boolean" ) {
266- return data . value ;
288+ if ( typeof value === "boolean" ) {
289+ return {
290+ t : "b" ,
291+ v : value ,
292+ w : value ? "TRUE" : "FALSE"
293+ } ;
267294 }
268295
269- if ( data . value instanceof Big ) {
270- return data . value . toNumber ( ) ;
296+ // Number (Big or JS number)
297+ if ( value instanceof Big || typeof value === "number" ) {
298+ const num = value instanceof Big ? value . toNumber ( ) : value ;
299+ return {
300+ t : "n" ,
301+ v : num ,
302+ z : '"$"#,##0.00_);\\("$"#,##0.00\\)' ,
303+ w : num . toLocaleString ( undefined , { minimumFractionDigits : 2 } )
304+ } ;
271305 }
272306
273- return data . displayValue ;
307+ // Default: string (ensure fallback is a string)
308+ return {
309+ t : "s" ,
310+ v : data . displayValue ?? "" ,
311+ w : data . displayValue ?? ""
312+ } ;
274313 } ,
275314
276315 dynamicText ( item , props ) {
277316 if ( props . dynamicText === undefined ) {
278- return "" ;
317+ return makeEmptyCell ( ) ;
279318 }
280319
281320 const data = props . dynamicText . get ( item ) ;
282321
283322 switch ( data . status ) {
284323 case "available" :
285- return data . value ;
324+ return { t : "s" , v : data . value ?? "" , w : data . value ?? "" } ;
286325 case "unavailable" :
287- return " n/a";
326+ return { t : "s" , v : " n/a", w : "n/a" } ;
288327 default :
289- return "" ;
328+ return makeEmptyCell ( ) ;
290329 }
291330 } ,
292331
293332 customContent ( item , props ) {
294- return props . exportValue ?. get ( item ) . value ?? "" ;
333+ const value = props . exportValue ?. get ( item ) . value ?? "" ;
334+ return { t : "s" , v : value , w : value } ;
295335 }
296336} ;
297337
338+ // Helper for empty cells
339+ function makeEmptyCell ( ) : ExcelCell {
340+ return { t : "s" , v : "" , w : "" } ;
341+ }
342+
298343function createRowReader ( columns : ColumnsType [ ] ) : RowReader {
299344 return item =>
300345 columns . map ( col => {
0 commit comments