Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PivotGrid - export using exceljs library (#13183)
- Loading branch information
1 parent
0e97f5c
commit c4c994d
Showing
7 changed files
with
2,332 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
import { isDefined, isString, isObject } from '../../core/utils/type'; // '../../core/utils/type'; | ||
import excelFormatConverter from '../excel_format_converter'; | ||
import messageLocalization from '../../localization/message'; | ||
import { extend } from '../../core/utils/extend'; // '../../core/utils/extend'; | ||
|
||
// docs.microsoft.com/en-us/office/troubleshoot/excel/determine-column-widths - "Description of how column widths are determined in Excel" | ||
const MAX_DIGIT_WIDTH_IN_PIXELS = 7; // Calibri font with 11pt size | ||
|
||
// support.office.com/en-us/article/change-the-column-width-and-row-height-72f5e3cc-994d-43e8-ae58-9774a0905f46 - "Column.Max - 255" | ||
// support.office.com/en-us/article/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3 - "Column width limit - 255 characters" | ||
const MAX_EXCEL_COLUMN_WIDTH = 255; | ||
|
||
function exportPivotGrid(options) { | ||
if(!isDefined(options)) return; | ||
|
||
const { | ||
customizeCell, | ||
component, | ||
worksheet, | ||
topLeftCell, | ||
keepColumnWidths, | ||
loadPanel | ||
} = _getFullOptions(options); | ||
|
||
const initialLoadPanelOptions = extend({}, component.option('loadPanel')); | ||
if('animation' in component.option('loadPanel')) { | ||
loadPanel.animation = null; | ||
} | ||
|
||
component.option('loadPanel', loadPanel); | ||
|
||
const wrapText = !!component.option('wordWrapEnabled'); | ||
|
||
worksheet.properties.outlineProperties = { | ||
summaryBelow: false, | ||
summaryRight: false | ||
}; | ||
|
||
const cellRange = { | ||
from: { row: topLeftCell.row, column: topLeftCell.column }, | ||
to: { row: topLeftCell.row, column: topLeftCell.column } | ||
}; | ||
|
||
const dataProvider = component.getDataProvider(); | ||
|
||
return new Promise((resolve) => { | ||
dataProvider.ready().done(() => { | ||
const columns = dataProvider.getColumns(); | ||
const dataRowsCount = dataProvider.getRowsCount(); | ||
|
||
if(keepColumnWidths) { | ||
_setColumnsWidth(worksheet, columns, cellRange.from.column); | ||
} | ||
|
||
const mergedCells = []; | ||
const mergeRanges = []; | ||
|
||
for(let rowIndex = 0; rowIndex < dataRowsCount; rowIndex++) { | ||
const row = worksheet.getRow(cellRange.from.row + rowIndex); | ||
|
||
_exportRow(rowIndex, columns.length, row, cellRange.from.column, dataProvider, customizeCell, undefined, mergedCells, mergeRanges, /* customizeCell, headerRowCount */ wrapText); | ||
|
||
if(rowIndex >= 1) { | ||
cellRange.to.row++; | ||
} | ||
} | ||
|
||
_mergeCells(worksheet, topLeftCell, mergeRanges); | ||
|
||
cellRange.to.column += columns.length > 0 ? columns.length - 1 : 0; | ||
|
||
const worksheetViewSettings = worksheet.views[0] || {}; | ||
|
||
if(component.option('rtlEnabled')) { | ||
worksheetViewSettings.rightToLeft = true; | ||
} | ||
|
||
if(Object.keys(worksheetViewSettings).indexOf('state') === -1) { | ||
extend(worksheetViewSettings, { state: 'frozen', xSplit: cellRange.from.column + dataProvider.getFrozenArea().x - 1, ySplit: cellRange.from.row + dataProvider.getFrozenArea().y - 1 }); | ||
} | ||
|
||
if(Object.keys(worksheetViewSettings).length > 0) { | ||
worksheet.views = [worksheetViewSettings]; | ||
} | ||
|
||
resolve(cellRange); | ||
}).always(() => { | ||
component.option('loadPanel', initialLoadPanelOptions); | ||
}); | ||
}); | ||
} | ||
|
||
function _getFullOptions(options) { | ||
const fullOptions = extend({}, options); | ||
if(!isDefined(fullOptions.topLeftCell)) { | ||
fullOptions.topLeftCell = { row: 1, column: 1 }; | ||
} else if(isString(fullOptions.topLeftCell)) { | ||
const { row, col } = fullOptions.worksheet.getCell(fullOptions.topLeftCell); | ||
fullOptions.topLeftCell = { row, column: col }; | ||
} | ||
if(!isDefined(fullOptions.keepColumnWidths)) { | ||
fullOptions.keepColumnWidths = true; | ||
} | ||
if(!isDefined(fullOptions.loadPanel)) { | ||
fullOptions.loadPanel = {}; | ||
} | ||
if(!isDefined(fullOptions.loadPanel.enabled)) { | ||
fullOptions.loadPanel.enabled = true; | ||
} | ||
if(!isDefined(fullOptions.loadPanel.text)) { | ||
fullOptions.loadPanel.text = messageLocalization.format('dxDataGrid-exporting'); | ||
} | ||
|
||
return fullOptions; | ||
} | ||
|
||
function _exportRow(rowIndex, cellCount, row, startColumnIndex, dataProvider, customizeCell, headerRowCount, mergedCells, mergeRanges, wrapText) { | ||
const styles = dataProvider.getStyles(); | ||
|
||
for(let cellIndex = 0; cellIndex < cellCount; cellIndex++) { | ||
const cellData = dataProvider.getCellData(rowIndex, cellIndex, true); | ||
const gridCell = cellData.cellSourceData; | ||
|
||
const excelCell = row.getCell(startColumnIndex + cellIndex); | ||
excelCell.value = cellData.value; | ||
|
||
|
||
if(isDefined(excelCell.value)) { | ||
const { alignment: horizontalAlignment, format, dataType } = styles[dataProvider.getStyleId(rowIndex, cellIndex)]; | ||
|
||
let numberFormat = _tryConvertToExcelNumberFormat(format, dataType); | ||
if(isDefined(numberFormat)) { | ||
numberFormat = numberFormat.replace(/"/g, ''); | ||
} else if(isString(excelCell.value) && /^[@=+-]/.test(excelCell.value)) { | ||
numberFormat = '@'; | ||
} | ||
|
||
_setNumberFormat(excelCell, numberFormat); | ||
_setAlignment(excelCell, wrapText, horizontalAlignment); | ||
} | ||
|
||
if(isDefined(customizeCell)) { | ||
customizeCell({ | ||
excelCell: excelCell, | ||
gridCell: gridCell | ||
}); | ||
} | ||
|
||
const mergeRange = _tryGetMergeRange(rowIndex, cellIndex, mergedCells, dataProvider); | ||
if(isDefined(mergeRange)) { | ||
mergeRanges.push(mergeRange); | ||
} | ||
} | ||
} | ||
|
||
function _setNumberFormat(excelCell, numberFormat) { | ||
excelCell.numFmt = numberFormat; | ||
} | ||
|
||
function _tryConvertToExcelNumberFormat(format, dataType) { | ||
const newFormat = _formatObjectConverter(format, dataType); | ||
const currency = newFormat.currency; | ||
|
||
format = newFormat.format; | ||
dataType = newFormat.dataType; | ||
|
||
return excelFormatConverter.convertFormat(format, newFormat.precision, dataType, currency); | ||
} | ||
|
||
function _formatObjectConverter(format, dataType) { | ||
const result = { | ||
format: format, | ||
precision: format && format.precision, | ||
dataType: dataType | ||
}; | ||
|
||
if(isObject(format)) { | ||
return extend(result, format, { | ||
format: format.formatter || format.type, | ||
currency: format.currency | ||
}); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
function _setAlignment(excelCell, wrapText, horizontalAlignment) { | ||
excelCell.alignment = excelCell.alignment || {}; | ||
|
||
if(isDefined(wrapText)) { | ||
excelCell.alignment.wrapText = wrapText; | ||
} | ||
if(isDefined(horizontalAlignment)) { | ||
excelCell.alignment.horizontal = horizontalAlignment; | ||
} | ||
|
||
excelCell.alignment.vertical = 'top'; | ||
} | ||
|
||
function _setColumnsWidth(worksheet, columns, startColumnIndex) { | ||
if(!isDefined(columns)) { | ||
return; | ||
} | ||
for(let i = 0; i < columns.length; i++) { | ||
const columnWidth = columns[i].width; | ||
|
||
if((typeof columnWidth === 'number') && isFinite(columnWidth)) { | ||
worksheet.getColumn(startColumnIndex + i).width = | ||
Math.min(MAX_EXCEL_COLUMN_WIDTH, Math.floor(columnWidth / MAX_DIGIT_WIDTH_IN_PIXELS * 100) / 100); | ||
} | ||
} | ||
} | ||
|
||
function _tryGetMergeRange(rowIndex, cellIndex, mergedCells, dataProvider) { | ||
if(!mergedCells[rowIndex] || !mergedCells[rowIndex][cellIndex]) { | ||
const cellMerge = dataProvider.getCellMerging(rowIndex, cellIndex); | ||
if(cellMerge.colspan || cellMerge.rowspan) { | ||
for(let i = rowIndex; i <= rowIndex + cellMerge.rowspan || 0; i++) { | ||
for(let j = cellIndex; j <= cellIndex + cellMerge.colspan || 0; j++) { | ||
if(!mergedCells[i]) { | ||
mergedCells[i] = []; | ||
} | ||
mergedCells[i][j] = true; | ||
} | ||
} | ||
return { | ||
start: { row: rowIndex, column: cellIndex }, | ||
end: { row: rowIndex + (cellMerge.rowspan || 0), column: cellIndex + (cellMerge.colspan || 0) } | ||
}; | ||
} | ||
} | ||
} | ||
|
||
function _mergeCells(worksheet, topLeftCell, mergeRanges) { | ||
mergeRanges.forEach((mergeRange) => { | ||
worksheet.mergeCells(mergeRange.start.row + topLeftCell.row, mergeRange.start.column + topLeftCell.column, mergeRange.end.row + topLeftCell.row, mergeRange.end.column + topLeftCell.column); | ||
}); | ||
} | ||
|
||
export { exportPivotGrid, _getFullOptions }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import './exceljsParts/exceljs.pivotGrid.tests.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.