Skip to content

Commit

Permalink
feat(ExcelExport): migrate to Excel-Export-Vanilla (ESM) (#1383)
Browse files Browse the repository at this point in the history
* feat(ExcelExport): migrate to Excel-Export-Vanilla (ESM)
  • Loading branch information
ghiscoding committed Feb 11, 2024
1 parent 2562352 commit f3838b3
Show file tree
Hide file tree
Showing 28 changed files with 606 additions and 846 deletions.
18 changes: 14 additions & 4 deletions docs/grid-functionalities/Export-to-Excel.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ You can Export to Excel, it will create an Excel file with the `.xlsx` default e
### Demo
[Demo Page](https://ghiscoding.github.io/slickgrid-universal/#/example02) / [Demo Component](https://github.com/ghiscoding/slickgrid-universal/blob/master/examples/webpack-demo-vanilla-bundle/src/examples/example02.ts)

### CSP (Content Security Policy)
Since we use the library `Excel-Builder-Vanilla`, which itself uses `fflate` as a dependency, that library uses Web Worker when it can which might throw a CSP error.

The easiest way to fix this problem is to modify your CSP header by adding the rule `worker-src 'self' blob:;`

```html
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; ...other rules... worker-src 'self' blob:;" />
```

### Grid Menu (hamburger menu)
The Grid Menu already has the "Export to Excel" enabled by default, so you will see it automatically in your Grid Menu. You still have the options to show/hide from the Grid Menu if you wish
- `hideExportExcelCommand` false by default, so it's optional
Expand Down Expand Up @@ -136,7 +146,7 @@ this.gridOptions = {
```
### Styling the Header Titles
By default the header titles (first row) will be styled as Bold text, however you can choose to style them differently with custom styles as shown below. To find out what styling you can use, you can take a look [Web Archive - Excel Builder](http://web.archive.org/web/20160907052007/http://excelbuilderjs.com/cookbook/fontsAndColors.html) website. The code shown below is used in [Aurelia-Slickgrid - Example 24](https://ghiscoding.github.io/aurelia-slickgrid/#/slickgrid/example24) if you wish to see the result.
By default the header titles (first row) will be styled as Bold text, however you can choose to style them differently with custom styles as shown below. To find out what styling you can use, you can take a look at Excel Builder-Vanilla [Documentation](https://ghiscoding.gitbook.io/excel-builder-vanilla/cookbook/fonts-and-colors) website. The code shown below is used in [Aurelia-Slickgrid - Example 24](https://ghiscoding.github.io/aurelia-slickgrid/#/slickgrid/example24) if you wish to see the result.
```ts
this.gridOptions = {
Expand All @@ -150,7 +160,7 @@ this.gridOptions = {
```
### Provide a Custom Header Title
You can optionally add a custom header title, you can see the UI Sample below, (that will be shown on first row of the Excel file) through the `customExcelHeader` callback method. We use the library `Excel-Builder` to create the export, however note that this library is no longer supported (but still the best) and the documentation site no longer exist but you can find all info on [Web Archive - Excel Builder](http://web.archive.org/web/20160907052007/http://excelbuilderjs.com/cookbook/fontsAndColors.html)
You can optionally add a custom header title, you can see the UI Sample below, (that will be shown on the first row of the Excel file) through the `customExcelHeader` callback method. We use the library `Excel-Builder-Vanilla` to create the export. Visit their [Documentation](https://ghiscoding.gitbook.io/excel-builder-vanilla/) website for more info.
The example below shows a title which uses a merged cell from "B1" to "D1" with a red bold color (pay attention to the color code, you need to add an extra "FF" in front of an html color code).
#### ViewModel
Expand All @@ -163,8 +173,8 @@ export class MyExample {
externalResources: [new ExcelExportService()],
excelExportOptions: {
// optionally pass a custom header to the Excel Sheet
// a lot of the info can be found on Web Archive of Excel-Builder
// http://web.archive.org/web/20160907052007/http://excelbuilderjs.com/cookbook/fontsAndColors.html
// a lot of the info can be found on Excel-Builder-Vanilla
// https://ghiscoding.gitbook.io/excel-builder-vanilla/cookbook/fonts-and-colors
customExcelHeader: (workbook, sheet) => {
const customTitle = this.translate.currentLang === 'fr' ? 'Titre qui est suffisament long pour être coupé' : 'My header that is long enough to wrap';
const stylesheet = workbook.getStyleSheet();
Expand Down
23 changes: 23 additions & 0 deletions docs/migrations/migration-to-4.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ With this new major version release, we can now say that the journey to moderniz
- TypeScript builds are now targeting ES2021 (bumped from ES2018)
- more CSP (Content Security Policy) safe
- you can create custom Formatter with native HTML element (to be more CSP safe), HTML strings are still accepted
- migrate to [Excel-Builder-Vanilla](https://github.com/ghiscoding/excel-builder-vanilla) (since >=4.4.0)
- for CSP safe, you also need to add `worker-src 'self' blob:;`

> **Note** for the full internal list of code changes applied in this release, you can take a look at the [Discussion - Roadmap to 4.0](https://github.com/ghiscoding/slickgrid-universal/discussions/1108)
Expand Down Expand Up @@ -124,6 +126,27 @@ Some of the DomUtils Service function were renamed, if you use any of them then
- `findFirstElementAttribute` renamed to `findFirstAttribute`
- `htmlEncodedStringWithPadding` renamed to `htmlEncodeWithPadding`

### Excel Export
_requires version >=4.4.0_

Migrate to a new [Excel-Builder-Vanilla](https://github.com/ghiscoding/excel-builder-vanilla) which is a fork of the `excel-builder.js` library. The new fork is all about modernizing Excel-Builder, it drops `Q`, `Lodash` and also replace `JSZip` to `fflate`.

By migrating from `JSZip` to `fflate`, the users should remove any `JSZip` references (like `tsconfig.json`)

```diff
{
"compilerOptions": {
- "paths": {
- "jszip": [
- "node_modules/jszip/dist/jszip.min.js"
- ]
- }
}
}
```

Also note that `fflate` could use Web Worker for performance reasons and by doing this you might have new CSP errors thrown. You simply need to add a CSP rule to avoid the error `worker-src 'self' blob:;`

## Formatters / CSP (Content Security Policy) Compliance
### Formatters Cleanup & Removals
I decided to remove a bunch of Formatters (like `Formatters.bold`, `Formatters.uppercase`, ...) because they could and should be using the column `cssClass` option. Basically, I did not myself use the best practice available when creating soo many Formatters and I did not realized that we could simply use `cssClass` which is a much more efficient way and so I'm correcting this inadvertence in this new release. With that in mind, I decided to do a big cleanup in the list of Formatters to make the project a little more lightweight with less code to support and replace some of them with more generic alternatives (see below).
Expand Down
1 change: 1 addition & 0 deletions examples/vite-demo-vanilla-bundle/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
script-src 'self' 'unsafe-inline';
script-src-elem 'self' gd.geobytes.com/AutoCompleteCity;
style-src 'self' 'unsafe-inline';
worker-src 'self' blob:;
">
</head>
<body>
Expand Down
4 changes: 2 additions & 2 deletions examples/vite-demo-vanilla-bundle/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AppRouting } from './app-routing';
import { Renderer } from './renderer';
import { ElementEventListener, RouterConfig } from './interfaces';
const pageLayoutGlobs = import.meta.glob('./examples/**/*.html', { as: 'raw', eager: true });
const pageLayoutGlobs = import.meta.glob('./examples/**/*.html', { query: '?raw', eager: true, import: 'default' });

export class App {
private _boundedEventWithListeners: ElementEventListener[] = [];
Expand Down Expand Up @@ -110,7 +110,7 @@ export class App {
}

// then load the new View
const htmlModule = pageLayoutGlobs[mapRoute.view];
const htmlModule = pageLayoutGlobs[mapRoute.view] as string;

if (htmlModule) {
this.renderer.loadView(htmlModule);
Expand Down
23 changes: 21 additions & 2 deletions examples/vite-demo-vanilla-bundle/src/examples/example02.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ export default class Example02 {
// collectionAsync: new Promise<any>(resolve => setTimeout(() => {
// resolve([{ value: '', label: '' }, { value: true, label: 'True' }, { value: false, label: 'False' }]);
// }, 250)),
}
},
excelExportOptions: { width: 11 }
}
];

Expand All @@ -198,7 +199,25 @@ export default class Example02 {
columnHeaderStyle: {
font: { color: 'FFFFFFFF' },
fill: { type: 'pattern', patternType: 'solid', fgColor: 'FF4a6c91' }
}
},

// optionally pass a custom header to the Excel Sheet
// a lot of the info can be found on Web Archive of Excel-Builder
// https://ghiscoding.gitbook.io/excel-builder-vanilla/cookbook/fonts-and-colors
customExcelHeader: (workbook, sheet) => {
const formatterId = workbook.getStyleSheet().createFormat({
// every color is prefixed with FF, then regular HTML color
font: { size: 18, fontName: 'Calibri', bold: true, color: 'FFFFFFFF' },
alignment: { wrapText: true, horizontal: 'center' },
fill: { type: 'pattern', patternType: 'solid', fgColor: 'FF203764' },
});
sheet.setRowInstructions(0, { height: 50 }); // change height of row 0

// excel cells start with A1 which is upper left corner
const customTitle = 'Grouping and Aggregator - My header is too long enough, so it will wrap';
sheet.mergeCells('A1', 'H1');
sheet.data.push([{ value: customTitle, metadata: { style: formatterId.id } }]);
},
},
textExportOptions: { filename: 'my-export', sanitizeDataExport: true },
externalResources: [this.excelExportService, new TextExportService()],
Expand Down
1 change: 1 addition & 0 deletions packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"autocompleter": "^9.1.2",
"dequal": "^2.0.3",
"dompurify": "^3.0.8",
"excel-builder-vanilla": "3.0.1",
"flatpickr": "^4.6.13",
"moment-mini": "^2.29.4",
"multiple-select-vanilla": "^1.2.5",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ExcelColumnMetadata, ExcelStyleInstruction } from 'excel-builder-vanilla';

import type { Column } from './column.interface';
import type { ExcelCellFormat } from './excelCellFormat.interface';
import type { GridOption } from './gridOption.interface';

/** Excel custom export options (formatting & width) that can be applied to a column */
Expand All @@ -11,7 +12,7 @@ export interface ColumnExcelExportOption {
* Option to provide custom Excel styling
* NOTE: this option will completely override any detected cell styling
*/
style?: ExcelCustomStyling;
style?: ExcelStyleInstruction;

/** Excel column width */
width?: number;
Expand All @@ -25,74 +26,11 @@ export interface GroupTotalExportOption {
* Option to provide custom Excel styling
* NOTE: this option will completely override any detected cell styling
*/
style?: ExcelCustomStyling;
style?: ExcelStyleInstruction;

/** Cell data value parser callback function */
valueParserCallback?: GetGroupTotalValueCallback;
}

export type GetDataValueCallback = (data: Date | string | number, columnDef: Column, excelFormatterId: number | undefined, excelStylesheet: unknown, gridOptions: GridOption) => Date | string | number | ExcelCellFormat;
export type GetDataValueCallback = (data: Date | string | number, columnDef: Column, excelFormatterId: number | undefined, excelStylesheet: unknown, gridOptions: GridOption) => Date | string | number | ExcelColumnMetadata;
export type GetGroupTotalValueCallback = (totals: any, columnDef: Column, groupType: string, excelStylesheet: unknown) => Date | string | number;

/**
* Excel Color in ARGB format, for color aren't transparent just use "FF" as prefix.
* For example if the color you want to add is a blue with HTML color "#0000FF", then the excel color we need to add is "FF0000FF"
* Online tool: https://www.myfixguide.com/color-converter/
*/
export type ExcelColorStyle = string | { theme: number; };
export interface ExcelAlignmentStyle {
horizontal?: 'center' | 'fill' | 'general' | 'justify' | 'left' | 'right';
justifyLastLine?: boolean;
readingOrder?: string;
relativeIndent?: boolean;
shrinkToFit?: boolean;
textRotation?: string | number;
vertical?: 'bottom' | 'distributed' | 'center' | 'justify' | 'top';
wrapText?: boolean;
}
export type ExcelBorderLine = 'continuous' | 'dash' | 'dashDot' | 'dashDotDot' | 'dotted' | 'double' | 'lineStyleNone' | 'medium' | 'slantDashDot' | 'thin' | 'thick';
export interface ExcelBorderStyle {
bottom?: { color?: ExcelColorStyle; style?: ExcelBorderLine; };
top?: { color?: ExcelColorStyle; style?: ExcelBorderLine; };
left?: { color?: ExcelColorStyle; style?: ExcelBorderLine; };
right?: { color?: ExcelColorStyle; style?: ExcelBorderLine; };
diagonal?: any;
outline?: boolean;
diagonalUp?: boolean;
diagonalDown?: boolean;
}
export interface ExcelFillStyle {
type?: 'gradient' | 'pattern';
patternType?: string;
degree?: number;
fgColor?: ExcelColorStyle;
start?: ExcelColorStyle;
end?: { pureAt?: number; color?: ExcelColorStyle; };
}
export interface ExcelFontStyle {
bold?: boolean;
color?: ExcelColorStyle;
fontName?: string;
italic?: boolean;
outline?: boolean;
size?: number;
strike?: boolean;
subscript?: boolean;
superscript?: boolean;
underline?: 'single' | 'double' | 'singleAccounting' | 'doubleAccounting';
}

/** Excel custom formatting that will be applied to a column */
export interface ExcelCustomStyling {
alignment?: ExcelAlignmentStyle;
border?: ExcelBorderStyle;
fill?: ExcelFillStyle;
font?: ExcelFontStyle;
format?: string;
protection?: {
locked?: boolean;
hidden?: boolean;
};
/** style id */
style?: number;
}
6 changes: 0 additions & 6 deletions packages/common/src/interfaces/excelCellFormat.interface.ts

This file was deleted.

8 changes: 3 additions & 5 deletions packages/common/src/interfaces/excelExportOption.interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import type { ExcelCustomStyling } from './columnExcelExportOption.interface';
import type { ExcelWorksheet } from './excelWorksheet.interface';
import type { ExcelWorkbook } from './excelWorkbook.interface';
import type { ExcelStyleInstruction, Worksheet, Workbook } from 'excel-builder-vanilla';
import type { FileType } from '../enums/fileType.enum';

export interface ExcelExportOption {
Expand All @@ -11,7 +9,7 @@ export interface ExcelExportOption {
autoDetectCellFormat?: boolean;

/** When defined, this will override header titles styling, when undefined the default will be a bold style */
columnHeaderStyle?: ExcelCustomStyling;
columnHeaderStyle?: ExcelStyleInstruction;

/** If set then this will be used as column width for all columns */
customColumnWidth?: number;
Expand Down Expand Up @@ -52,5 +50,5 @@ export interface ExcelExportOption {
sheetName?: string;

/** Add a Custom Excel Header on first row of the Excel Sheet */
customExcelHeader?: (workbook: ExcelWorkbook, sheet: ExcelWorksheet) => void;
customExcelHeader?: (workbook: Workbook, sheet: Worksheet) => void;
}
9 changes: 0 additions & 9 deletions packages/common/src/interfaces/excelMetadata.interface.ts

This file was deleted.

32 changes: 0 additions & 32 deletions packages/common/src/interfaces/excelStylesheet.interface.ts

This file was deleted.

17 changes: 0 additions & 17 deletions packages/common/src/interfaces/excelWorkbook.interface.ts

This file was deleted.

36 changes: 0 additions & 36 deletions packages/common/src/interfaces/excelWorksheet.interface.ts

This file was deleted.

5 changes: 0 additions & 5 deletions packages/common/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,8 @@ export * from './editorValidator.interface';
export * from './editUndoRedoBuffer.interface';
export * from './elementPosition.interface';
export * from './emptyWarning.interface';
export * from './excelCellFormat.interface';
export * from './excelCopyBufferOption.interface';
export * from './excelExportOption.interface';
export * from './excelMetadata.interface';
export * from './excelStylesheet.interface';
export * from './excelWorkbook.interface';
export * from './excelWorksheet.interface';
export * from './extension.interface';
export * from './extensionModel.interface';
export * from './externalCopyClipCommand.interface';
Expand Down
Loading

0 comments on commit f3838b3

Please sign in to comment.