Skip to content

Commit 487932b

Browse files
committed
feat: implement parametric schema-level formatters
1 parent b0af7aa commit 487932b

File tree

7 files changed

+35
-12
lines changed

7 files changed

+35
-12
lines changed

examples/financial-report.xlsx

7 Bytes
Binary file not shown.

examples/kitchen-sink.xlsx

1.37 KB
Binary file not shown.

examples/playground.xlsx

8.46 MB
Binary file not shown.

src/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable ts/ban-types */
22
import XLSX, { type WorkSheet, utils } from 'xlsx-js-style'
3-
import type { CellValue, Column, ColumnGroup, ExcelBuildOutput, ExcelBuildParams, ExcelSchema, FormattersMap, GenericObject, NestedPaths, Not, SchemaColumnKeys, SheetConfig, SheetParams, SheetTable, SheetTableBuilder, TOutputType, TransformersMap } from './types'
3+
import type { CellValue, Column, ColumnGroup, ExcelBuildOutput, ExcelBuildParams, ExcelSchema, FormatterPreset, FormattersMap, GenericObject, NestedPaths, Not, SchemaColumnKeys, SheetConfig, SheetParams, SheetTable, SheetTableBuilder, TOutputType, TransformersMap } from './types'
44
import { SheetCacheManager, applyGroupBorders, buildSheetConfig, createCell, getColumnHeaderStyle, getColumnSeparatorIndexes, getWorksheetColumnWidths, tableHasSummary } from './utils'
55

66
export type * from './types'
@@ -26,17 +26,21 @@ export class ExcelSchemaBuilder<
2626
return this as unknown as ExcelSchemaBuilder<T, CellKeyPaths, UsedKeys, TransformMap & Transformers, FormatMap, ContextMap>
2727
}
2828

29-
withFormatters<Formatters extends FormattersMap>(formatters: Formatters): ExcelSchemaBuilder<T, CellKeyPaths, UsedKeys, TransformMap, FormatMap & Formatters, ContextMap> {
29+
withFormatters<
30+
Formatters extends FormattersMap,
31+
>(formatters: Formatters,
32+
): ExcelSchemaBuilder<T, CellKeyPaths, UsedKeys, TransformMap, FormatMap & Formatters, ContextMap> {
3033
this.formatters = formatters as FormatMap & Formatters
3134
return this as unknown as ExcelSchemaBuilder<T, CellKeyPaths, UsedKeys, TransformMap, FormatMap & Formatters, ContextMap>
3235
}
3336

3437
public column<
3538
K extends string,
3639
FieldValue extends CellKeyPaths | ((data: T) => CellValue),
40+
Preset extends FormatterPreset<FormatMap>[keyof FormatMap],
3741
>(
3842
columnKey: Not<K, UsedKeys>,
39-
column: Omit<Column<T, FieldValue, K, TransformMap, FormatMap>, 'columnKey' | 'type'>,
43+
column: Omit<Column<T, FieldValue, K, TransformMap, FormatMap, Preset>, 'columnKey' | 'type'>,
4044
): ExcelSchemaBuilder<T, CellKeyPaths, UsedKeys | K, TransformMap, FormatMap, ContextMap> {
4145
if (this.columns.some(c => c.columnKey === columnKey))
4246
throw new Error(`Column with key '${columnKey}' already exists.`)

src/types.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,18 @@ export interface TransformersMap {
5757
[key: string]: ValueTransformer
5858
}
5959

60+
export type FormatterFunction = (params: any) => string
61+
6062
export interface FormattersMap {
61-
[key: string]: string
63+
[key: string]: FormatterFunction | string
64+
}
65+
66+
export type FormatterPreset<T extends FormattersMap> = {
67+
[Key in keyof T]: ({ preset: Key } & (T[Key] extends infer P
68+
? P extends (params: any) => any
69+
? { params: Parameters<P>[0] }
70+
: {}
71+
: {}))
6272
}
6373

6474
export type NonNullableDeep<T> = T extends null | undefined ? never : T
@@ -81,13 +91,14 @@ export type Column<
8191
ColKey extends string,
8292
TransformMap extends TransformersMap,
8393
FormatMap extends FormattersMap,
94+
Preset extends FormatterPreset<FormatMap>[keyof FormatMap] = never,
8495
> = {
8596
type: 'column'
8697
label?: string
8798
columnKey: ColKey
8899
key: FieldValue
89100
default?: CellValue
90-
format?: string | { preset: keyof FormatMap } | ((rowData: T, rowIndex: number, subRowIndex: number) => string | { preset: keyof FormatMap })
101+
format?: Preset | string | ((rowData: T, rowIndex: number, subRowIndex: number) => string | Preset)
91102
cellStyle?: CellStyle | ((rowData: T, rowIndex: number, subRowIndex: number) => CellStyle)
92103
headerStyle?: CellStyle
93104
summary?: Array<{

src/utils.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { utils } from 'xlsx-js-style'
22
import type XLSX from 'xlsx-js-style'
33
import type { CellStyle, ExcelDataType, WorkSheet } from 'xlsx-js-style'
44
import { deepmerge } from 'deepmerge-ts'
5-
import type { BaseCellValue, CellValue, Column, FormattersMap, GenericObject, SheetConfig, ValueTransformer } from './types'
5+
import type { BaseCellValue, CellValue, Column, FormatterPreset, FormattersMap, GenericObject, SheetConfig, ValueTransformer } from './types'
66
import { THICK_BORDER_STYLE, THIN_BORDER_STYLE } from './const'
77

88
export function getPropertyFromPath(obj: GenericObject, path: string) {
@@ -335,7 +335,7 @@ export function createCell(params: {
335335
data?: GenericObject
336336
value?: BaseCellValue
337337
style?: CellStyle | ((rowData: any, rowIndex: number, subRowIndex: number) => CellStyle)
338-
format?: string | { preset: string | number | symbol } | ((rowData: any, rowIndex: number, subRowIndex: number) => string | { preset: string | number | symbol })
338+
format?: string | FormatterPreset<any> | ((rowData: any, rowIndex: number, subRowIndex: number) => string | FormatterPreset<any>)
339339
extraStyle?: CellStyle
340340
bordered?: boolean
341341
rowIndex?: number
@@ -352,8 +352,13 @@ export function createCell(params: {
352352
const format = typeof rawFormat === 'string'
353353
? rawFormat
354354
: rawFormat?.preset
355-
? params.formatPresets[rawFormat.preset as string]
356-
: ''
355+
? params.formatPresets[rawFormat.preset as unknown as string]
356+
? typeof params.formatPresets[rawFormat.preset as unknown as string] === 'function'
357+
? (params.formatPresets[rawFormat.preset as unknown as string] as Function)(rawFormat.params)
358+
: params.formatPresets[rawFormat.preset as unknown as string]
359+
: ''
360+
: rawFormat
361+
357362
return {
358363
v: params.value === null ? '' : params.value,
359364
t: getCellDataType(params.value),

test/play.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import fs from 'node:fs'
22
import { describe, it } from 'vitest'
33
import { faker } from '@faker-js/faker'
4+
import type { FormattersMap } from '../src'
45
import { ExcelBuilder, ExcelSchemaBuilder } from '../src'
56

67
describe('should generate the play excel file', () => {
78
it('exported', () => {
89
interface User { id: string, name: string, birthDate: Date, balance: number }
9-
1010
// Group definition within the schema
1111
const schema = ExcelSchemaBuilder.create<User>()
1212
.withFormatters({
1313
date: 'd mmm yyyy',
14-
currency: '$#,##0.00',
14+
currency: (params: { currency: string }) => `${params.currency}#,##0.00`,
15+
other: (params: { other: string }) => `${params.other}#,##0.00`,
1516
})
1617
.column('id', { key: 'id' })
1718
.column('name', {
@@ -20,7 +21,9 @@ describe('should generate the play excel file', () => {
2021
headerStyle: { fill: { fgColor: { rgb: '00FF00' } } },
2122
})
2223
.column('birthDate', { key: 'birthDate', format: { preset: 'date' } })
23-
.column('balance', { key: 'balance', format: { preset: 'currency' } })
24+
.column('birthDate2', { key: 'birthDate', format: 'd mmm yyyy' })
25+
.column('balanceUsd', { key: 'balance', format: { preset: 'currency', params: { currency: '$' } } })
26+
.column('balanceEur', { key: 'balance', format: { preset: 'currency', params: { currency: '€' } } })
2427
.build()
2528

2629
const users: User[] = Array.from({ length: 100000 }, (_, i) => ({

0 commit comments

Comments
 (0)