UNPKG

@adaptabletools/adaptable

Version:

Powerful AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements

859 lines (858 loc) 41.5 kB
import tinycolor from 'tinycolor2'; import { inferSchema, initParser } from 'udsv'; import { resolveDisplayFormat } from '../AdaptableState/Common/AdaptableFormatPresets'; import { createUuid } from '../AdaptableState/Uuid'; import { AG_GRID_GROUPED_COLUMN, AG_GRID_SELECTION_COLUMN, } from '../Utilities/Constants/GeneralConstants'; import { ExportModuleId } from '../Utilities/Constants/ModuleConstants'; import StringExtensions from '../Utilities/Extensions/StringExtensions'; import FormatHelper, { DateFormatter } from '../Utilities/Helpers/DisplayFormatHelper'; import { convertCSSAbsoluteFontSizeToPt, getVariableColor, sanitizeStyle, } from '../Utilities/Helpers/StyleHelper'; import { waitForTimeout } from '../Utilities/waitForTimeout'; export class AgGridExportAdapter { _adaptableInstance; DANGER_excelStyles = []; originalExcelStyles = []; excelStylesCache = {}; cellOrHeaderClassKey2ExcelStyleIdMap = {}; excelStylesWithFormattedDate = {}; constructor(_adaptableInstance) { this._adaptableInstance = _adaptableInstance; } get agGridApi() { return this._adaptableInstance.agGridAdapter.getAgGridApi(); } get adaptableApi() { return this._adaptableInstance.api; } get exportOptions() { return this._adaptableInstance.api.optionsApi.getExportOptions(); } get logger() { return this._adaptableInstance.logger; } static getExcelClassNameForCell(colId, primaryKeyValue, userDefinedCellClass) { let excelClassName = `--excel-cell-${colId}-${primaryKeyValue}`; if (excelClassName.indexOf(' ') > 0) { excelClassName = excelClassName.replace(/\s/g, '_'); } return userDefinedCellClass != null ? `${excelClassName}-${Array.isArray(userDefinedCellClass) ? userDefinedCellClass.join('-') : userDefinedCellClass}` : excelClassName; } static getExcelClassNameForHeader(colId, userDefinedHeaderClass) { let excelClassName = `--excel-header-${colId}`; if (excelClassName.indexOf(' ') > 0) { excelClassName = excelClassName.replace(/\s/g, '_'); } return userDefinedHeaderClass != null ? `${excelClassName}-${Array.isArray(userDefinedHeaderClass) ? userDefinedHeaderClass.join('-') : userDefinedHeaderClass}` : excelClassName; } destroy() { this.excelStylesCache = null; this.cellOrHeaderClassKey2ExcelStyleIdMap = null; this.excelStylesWithFormattedDate = null; } async exportData(config) { const { report, format, showProgressIndicator } = config; let excelStylesWerePatched = false; try { if (showProgressIndicator) { this.adaptableApi.userInterfaceApi.showProgressIndicator({ text: `Exporting ${report.Name}...`, }); await waitForTimeout(16); } this.adaptableApi.exportApi.internalApi.setExportInProgress(config.report.Name, config.format, config.destination); const exportProcessData = this.buildExportProcessData(config); const { exportContext, exportParams } = exportProcessData; if (this.adaptableApi.layoutApi.isCurrentLayoutPivot() && config.report.ReportRowScope === 'AllRows') { return this.adaptableApi.exportApi.internalApi.exportAllDataInPivotMode(exportProcessData); } if (exportContext.isVisualExcelReport) { this.patchExcelStyles(exportContext); excelStylesWerePatched = true; } if (exportContext.destination === 'Download' && exportContext.isExcelReport) { this.agGridApi.exportDataAsExcel(exportParams); return; } if (exportContext.destination === 'Download' && format === 'CSV') { this.agGridApi.exportDataAsCsv(exportParams); return; } if (exportContext.isExcelReport) { return { type: 'excel', data: this.agGridApi.getDataAsExcel(exportParams), }; } const csvReportData = this.agGridApi.getDataAsCsv(exportParams); if (exportContext.format === 'CSV') { return { type: 'csv', data: csvReportData, }; } const jsonReportData = this.mapCsvToJson(csvReportData, exportContext); return { type: 'json', data: jsonReportData, }; } catch (error) { this.logger.consoleError(`Export failed: report="${report.Name}", format="${format}", destination="${config.destination}".`, error.stack); } finally { this.adaptableApi.exportApi.internalApi.setExportComplete(); if (excelStylesWerePatched) { this.resetExcelStyles(); } if (showProgressIndicator) { this.adaptableApi.userInterfaceApi.hideProgressIndicator(); } } } buildExportProcessData(config) { const exportContext = this.buildExportProcessContext(config); let exportParams = this.buildExportParams(exportContext); if (typeof config?.customExportParams === 'function') { this.logger.info(`Applying custom export params: report="${config.report.Name}", format="${config.format}".`); exportParams = config.customExportParams(exportParams); } exportContext.exportedColumnIds = exportParams.columnKeys; return { exportContext, exportParams, }; } buildExportParams(exportContext) { const baseExportParams = this.buildBaseExportParams(exportContext); if (exportContext.format === 'Excel' || exportContext.format === 'VisualExcel') { const excelExportParams = baseExportParams; const sheetName = typeof this.exportOptions.excelSheetName === 'function' ? this.exportOptions.excelSheetName(this.adaptableApi.exportApi.internalApi.buildBaseExportContext(exportContext.report.Name, exportContext.format, exportContext.destination)) : this.exportOptions.excelSheetName; excelExportParams.sheetName = sheetName; return excelExportParams; } const csvExportParams = baseExportParams; csvExportParams.columnSeparator = this.getCsvSeparator(exportContext); return csvExportParams; } buildBaseExportParams(exportContext) { const params = {}; const { columnKeys, exportedRows, shouldRowBeSkipped, skipRowGroups, rowGroupExpandState } = this.getReportColsAndRows(exportContext); params.columnKeys = columnKeys; params.exportedRows = exportedRows; params.shouldRowBeSkipped = shouldRowBeSkipped; params.skipRowGroups = skipRowGroups; params.rowGroupExpandState = rowGroupExpandState; params.fileName = this.adaptableApi.exportApi.internalApi.getReportFileName(exportContext.report.Name, exportContext.format, exportContext.destination); params.skipColumnHeaders = this.computeSkipColumnHeaders(exportContext); if (!exportContext.isExcelReport && exportContext.format !== 'CSV') { params.skipColumnHeaders = false; } params.getCustomContentBelowRow = this.computeGetCustomContentBelowRow(exportContext); params.processCellCallback = this.computeProcessCellCallback(exportContext); params.processRowGroupCallback = this.computeProcessRowGroupCallback(exportContext); return params; } computeProcessRowGroupCallback(exportContext) { return (params) => { let rowGroupNode = params.node; const agColumn = params.column; const rowGroupSummary = [ this.processRowGroupForExcelExport(rowGroupNode, exportContext, agColumn) ?? '', ]; while (rowGroupNode.parent) { rowGroupNode = rowGroupNode.parent; const formattedParentNode = this.processRowGroupForExcelExport(rowGroupNode, exportContext, agColumn); if (formattedParentNode) { rowGroupSummary.push(formattedParentNode); } } let summary = rowGroupSummary.reverse().join(' -> '); if (agColumn?.getColId() === AG_GRID_GROUPED_COLUMN && rowGroupNode.footer) { summary = `Total: ${summary}`; } return summary; }; } computeProcessCellCallback(exportContext) { return (params) => { const { node, column, value } = params; const colId = column.getColId(); if (exportContext.report.ReportRowScope === 'SelectedRows') { if (!exportContext.isCellPartOfSelection(params.node.id, colId)) return ''; } const columnId = column.getColId(); if (node?.group && (this.adaptableApi.columnApi.isAutoRowGroupColumn(columnId) || node?.aggData?.[columnId] == undefined)) { return value; } return this.processCellForExcelExport(node, columnId, exportContext); }; } getReportColsAndRows(exportContext) { const { report, exportedColumnIds, isExportingVisibleColumnToJSON } = exportContext; const columnKeys = exportedColumnIds; let exportedRows = 'all'; if (report.ReportRowScope === 'VisibleRows' || report.ReportRowScope === 'SelectedRows') { exportedRows = 'filteredAndSorted'; } let shouldRowBeSkipped; if (report.ReportRowScope === 'SelectedRows') { const selectedRowColumnMapping = new Map(); this.adaptableApi.gridApi.getSelectedRowInfo().gridRows?.forEach((row) => { if (row?.rowNode?.id) { selectedRowColumnMapping.set(row.rowNode.id, true); } }); this.adaptableApi.gridApi.getSelectedCellInfo().gridCells?.map((gridCell) => { if (gridCell.rowNode?.id) { const selectedColumns = selectedRowColumnMapping.get(gridCell.rowNode.id); if (selectedColumns == null) { selectedRowColumnMapping.set(gridCell.rowNode.id, new Set([gridCell.column.columnId])); } else if (selectedColumns instanceof Set) { selectedColumns.add(gridCell.column.columnId); } } }); shouldRowBeSkipped = (params) => { return !selectedRowColumnMapping.has(params.node.id); }; exportContext.isCellPartOfSelection = (rowId, columnId) => { const selectedRow = selectedRowColumnMapping.get(rowId); if (selectedRow === true) { return true; } if (selectedRow instanceof Set) { return selectedRow.has(columnId); } return false; }; } if (report.ReportRowScope === 'ExpressionRows') { shouldRowBeSkipped = (params) => { const rowNodeMatchesExpression = this.adaptableApi.internalApi .getQueryLanguageService() .evaluateBooleanExpression(report?.Query?.BooleanExpression, ExportModuleId, params.node); return !rowNodeMatchesExpression; }; } let skipRowGroups; if (report.ReportRowScope === 'AllRows' || isExportingVisibleColumnToJSON) { skipRowGroups = true; } if (this.isTreeDataGrid()) { skipRowGroups = false; } let rowGroupExpandState = 'expanded'; if (report.ReportRowScope === 'VisibleRows') { rowGroupExpandState = 'match'; } return { columnKeys, exportedRows, shouldRowBeSkipped, skipRowGroups, rowGroupExpandState }; } isTreeDataGrid() { return this.adaptableApi.gridApi.isTreeDataGrid(); } buildExportProcessContext(config) { const { report } = config; const currentLayout = this.adaptableApi.layoutApi.getCurrentLayout(); const groupColumnIds = this.isTreeDataGrid() ? [AG_GRID_GROUPED_COLUMN] : currentLayout.RowGroupedColumns ?? currentLayout.PivotGroupedColumns; const pivotColumnIds = currentLayout.PivotColumns; const isExportingVisibleColumnToJSON = config.format === 'JSON' && report.ReportColumnScope === 'VisibleColumns'; const exportableColumnIds = this.adaptableApi.columnApi .getExportableColumns() .map((col) => col.columnId); const exportableColumnIdsSet = new Set(exportableColumnIds); const onlyExportableColumnIds = (columnId) => exportableColumnIdsSet.has(columnId); const getVisibleColumnIds = () => { return this.adaptableApi.layoutApi.isCurrentLayoutPivot() ? this.adaptableApi.layoutApi .getCurrentVisibleColumnIdsForPivotLayout() .filter(onlyExportableColumnIds) : this.adaptableApi.layoutApi .getCurrentVisibleColumnIdsForTableLayout() .filter(onlyExportableColumnIds); }; let exportedColumnIds = []; switch (report.ReportColumnScope) { case 'AllColumns': exportedColumnIds = exportableColumnIds.filter((colId) => this.isTreeDataGrid() || (!this.adaptableApi.columnApi.isAutoRowGroupColumn(colId) && !this.adaptableApi.columnApi.isSelectionColumn(colId))); break; case 'VisibleColumns': exportedColumnIds = getVisibleColumnIds(); if (isExportingVisibleColumnToJSON) { exportedColumnIds = [ ...groupColumnIds, ...exportedColumnIds.filter((columnId) => this.isTreeDataGrid() || (!this.adaptableApi.columnApi.isAutoRowGroupColumn(columnId) && !this.adaptableApi.columnApi.isSelectionColumn(columnId))), ]; } break; case 'SelectedColumns': const selectedRowInfo = this.adaptableApi.gridApi.getSelectedRowInfo(); if (selectedRowInfo?.gridRows?.length) { exportedColumnIds = getVisibleColumnIds(); } else { const selectedColumnIds = this.adaptableApi.gridApi .getSelectedCellInfo() .columns?.map((column) => column.columnId); const selectedColumnIdsSet = new Set(selectedColumnIds.filter(onlyExportableColumnIds)); exportedColumnIds = getVisibleColumnIds().filter((colId) => selectedColumnIdsSet.has(colId)); } break; case 'ScopeColumns': if ('ColumnIds' in report.Scope) { exportedColumnIds = report.Scope.ColumnIds?.filter(onlyExportableColumnIds); } else { exportedColumnIds = this.adaptableApi.columnScopeApi .getColumnsInScope(report.Scope) .map((col) => col.columnId) .filter(onlyExportableColumnIds); } break; default: this.adaptableApi.logError('Invalid ReportColumnScope: ', report.ReportColumnScope); exportedColumnIds = exportableColumnIds; } return { ...config, exportedColumnIds, isExcelReport: config.format === 'Excel' || config.format === 'VisualExcel', isVisualExcelReport: config.format === 'VisualExcel', isExportingVisibleColumnToJSON, isCellPartOfSelection: () => false, getCurrent: () => { return { layout: currentLayout, groupColumnIds, pivotColumnIds, }; }, }; } processCellForExcelExport(rowNode, columnId, exportContext) { if (this.adaptableApi.exportApi.internalApi.isVisualDataExportInProgress()) { const cellKey = AgGridExportAdapter.getExcelClassNameForCell(columnId, this.adaptableApi.gridApi.getPrimaryKeyValueForRowNode(rowNode)); const isoFormattedDate = this.getExcelStyleWithFormattedDate(cellKey); if (isoFormattedDate) { return isoFormattedDate; } } return this.getCellExportValueFromRawValue(rowNode, this.adaptableApi.gridApi.getRawValueFromRowNode(rowNode, columnId), columnId, exportContext); } processRowGroupForExcelExport(rowNode, exportContext, agColumn) { if (this.isTreeDataGrid()) { return rowNode.key; } const getColIdAndRawValue = () => { if (this.adaptableApi.columnApi.isAutoRowGroupColumn(agColumn?.getColId())) { const columnId = agColumn.getColId(); let rawValue = rowNode.key; return { columnId, rawValue }; } if (!!rowNode.aggData) { const columnId = agColumn.getColId(); const rawValue = rowNode.aggData?.[columnId]; return { columnId, rawValue }; } return {}; }; let { columnId, rawValue } = getColIdAndRawValue(); if (!columnId || rawValue == null) { return; } const columnDataType = this.adaptableApi.columnApi.getColumnDataTypeForColumnId(columnId); if (columnDataType === 'date' && typeof rawValue === 'string' && /^\d+$/.test(rawValue)) { const dateRawValue = parseInt(rawValue); if (dateRawValue != undefined) { rawValue = dateRawValue; } } return this.getCellExportValueFromRawValue(rowNode, rawValue, columnId, exportContext); } patchExcelStyles(exportContext) { const exportExcelStyles = this.buildExcelStyles(exportContext); this.DANGER_excelStyles.push(...exportExcelStyles); } resetExcelStyles() { this.DANGER_excelStyles.splice(0, this.DANGER_excelStyles.length, ...this.originalExcelStyles); this.excelStylesCache = {}; this.cellOrHeaderClassKey2ExcelStyleIdMap = {}; this.excelStylesWithFormattedDate = {}; } buildExcelStyles(exportContext) { this.resetExcelStyles(); const adaptableColumnMemo = {}; const getAdaptableColumnWithColumnId = (columnId) => { const memoizedColumn = adaptableColumnMemo[columnId]; if (memoizedColumn) { return memoizedColumn; } const abColumn = this.adaptableApi.columnApi.getColumnWithColumnId(columnId); adaptableColumnMemo[columnId] = abColumn; return abColumn; }; const formatColumnsWithDisplayFormatMemo = {}; const getFormatColumnsWithDisplayFormatForColumn = (columnId) => { const memoizedFormatColumns = formatColumnsWithDisplayFormatMemo[columnId]; if (memoizedFormatColumns) { return memoizedFormatColumns; } const abColumn = getAdaptableColumnWithColumnId(columnId); const formatColumns = this.adaptableApi.formatColumnApi.internalApi.getFormatColumnsWithDisplayFormatForColumn(abColumn, { target: 'cell' }); formatColumnsWithDisplayFormatMemo[columnId] = formatColumns; return formatColumns; }; const agGridDisplayedColumns = this.agGridApi .getAllDisplayedColumns() .filter((agCol) => agCol.getId() !== AG_GRID_SELECTION_COLUMN); const colDefs = agGridDisplayedColumns.map((column) => { return column.getColDef(); }); const forAllVisibleRowNodesDoConfig = { includeGroupRows: true }; const userExcelStyles = this.DANGER_excelStyles; agGridDisplayedColumns.forEach((column, columnIndex) => { const colDef = colDefs[columnIndex]; const columnId = column.getId(); const adaptableColumn = getAdaptableColumnWithColumnId(columnId); if (!adaptableColumn) { this.logger.warn(`Export styling: column "${columnId}" not found in Adaptable.`); return; } let headerClassParams = { colDef, column, floatingFilter: false, api: this.agGridApi, context: this.agGridApi.getGridOption('context'), }; const headerStyle = typeof colDef.headerStyle === 'function' ? colDef.headerStyle(headerClassParams) : {}; const excelHeaderStyles = []; let userColDefHeaderClass = this._adaptableInstance.agGridColumnAdapter.getUserColDefProperty(column.getColId(), 'headerClass'); const userDefinedHeaderClass = typeof userColDefHeaderClass === 'function' ? userColDefHeaderClass(headerClassParams) : userColDefHeaderClass; const userDefinedExcelHeaderStyle = userDefinedHeaderClass && userExcelStyles.find((excelStyle) => { return typeof userDefinedHeaderClass === 'string' ? userDefinedHeaderClass === excelStyle.id : userDefinedHeaderClass?.includes?.(excelStyle.id); }); if (userDefinedExcelHeaderStyle) { excelHeaderStyles.push(userDefinedExcelHeaderStyle); } const adaptableHeaderStyle = { ...Object.keys(headerStyle).reduce((result, key) => { if (headerStyle[key] !== null) { result[key] = headerStyle[key]; } return result; }, {}), }; const sanitizedAdaptableHeaderStyle = sanitizeStyle(adaptableHeaderStyle); if (Object.values(sanitizedAdaptableHeaderStyle).some((style) => style != null)) { excelHeaderStyles.push(this.convertCSSToExcelStyle(sanitizedAdaptableHeaderStyle)); } if (!excelHeaderStyles.length) { return; } const headerClassId = AgGridExportAdapter.getExcelClassNameForHeader(column.getId(), userDefinedHeaderClass); const finalHeaderExcelStyle = Object.assign({}, ...excelHeaderStyles); this.registerExcelStyle(finalHeaderExcelStyle, headerClassId); }); this.adaptableApi.internalApi.forAllVisibleRowNodesDo((node, rowIndex) => { const rowParams = { node, data: node.data, rowIndex, api: this.agGridApi, context: this.agGridApi.getGridOption('context') || {}, }; const getRowStyleFn = this.agGridApi.getGridOption('getRowStyle'); const rowStyle = getRowStyleFn ? getRowStyleFn(rowParams) : {}; agGridDisplayedColumns.forEach((column, columnIndex) => { const colDef = colDefs[columnIndex]; const columnId = column.getId(); const adaptableColumn = getAdaptableColumnWithColumnId(columnId); if (!adaptableColumn) { this.logger.warn(`Export styling: column "${columnId}" not found in Adaptable.`); return; } const isDateCellExportedAsFormattedValue = this.isDateCellExportedAsFormattedValue(adaptableColumn, exportContext); let cellClassParams; const getLazyCellClassParams = () => { if (!cellClassParams) { cellClassParams = { colDef, node, column, data: node.data, value: this.adaptableApi.gridApi.getRawValueFromRowNode(node, columnId), rowIndex, api: this.agGridApi, context: {}, }; } return cellClassParams; }; const cellStyle = typeof colDef.cellStyle === 'function' ? colDef.cellStyle(getLazyCellClassParams()) : {}; const excelCellStyles = []; let userColDefCellClass = this._adaptableInstance.agGridColumnAdapter.getUserColDefProperty(column.getColId(), 'cellClass'); const userDefinedCellClass = typeof userColDefCellClass === 'function' ? userColDefCellClass(getLazyCellClassParams()) : userColDefCellClass; const userDefinedExcelStyle = userDefinedCellClass && userExcelStyles.find((excelStyle) => { return typeof userDefinedCellClass === 'string' ? userDefinedCellClass === excelStyle.id : userDefinedCellClass?.includes?.(excelStyle.id); }); if (userDefinedExcelStyle) { excelCellStyles.push(userDefinedExcelStyle); } const adaptableCellStyle = { ...rowStyle, ...Object.keys(cellStyle).reduce((result, key) => { if (cellStyle[key] !== null) { result[key] = cellStyle[key]; } return result; }, {}), }; const sanitizedAdaptableCellStyle = sanitizeStyle(adaptableCellStyle); if (Object.values(sanitizedAdaptableCellStyle).some((style) => style != null)) { excelCellStyles.push(this.convertCSSToExcelStyle(sanitizedAdaptableCellStyle)); } const excelDataType = this.getExcelDataType(adaptableColumn.dataType); const rawValue = this.adaptableApi.gridApi.getRawValueFromRowNode(node, column.getId()); if (!excelCellStyles.length && !(excelDataType === 'DateTime' && isDateCellExportedAsFormattedValue)) { return; } const cellClassId = AgGridExportAdapter.getExcelClassNameForCell(column.getId(), this.adaptableApi.gridApi.getPrimaryKeyValueForRowNode(node), userDefinedCellClass); const finalCellExcelStyle = Object.assign({}, ...excelCellStyles); if (excelDataType === 'DateTime' && isDateCellExportedAsFormattedValue) { let dateFormatPattern = this.exportOptions.exportDateFormat; const abColumn = getAdaptableColumnWithColumnId(column.getColId()); if (!dateFormatPattern) { const mostRelevantFormatColumn = this.adaptableApi.formatColumnApi.internalApi.getMostRelevantFormatColumnForColumn(getFormatColumnsWithDisplayFormatForColumn(column.getColId()), abColumn, { node, value: rawValue }); const resolvedDisplayFormat = resolveDisplayFormat(mostRelevantFormatColumn?.DisplayFormat); dateFormatPattern = resolvedDisplayFormat?.Formatter === 'DateFormatter' && resolvedDisplayFormat?.Options?.Pattern; } if (dateFormatPattern) { const customDateFormatPattern = typeof dateFormatPattern === 'function' ? dateFormatPattern({ ...this.adaptableApi.exportApi.internalApi.buildBaseExportContext(exportContext.report.Name, exportContext.format, exportContext.destination), column: adaptableColumn, }) : dateFormatPattern; const normalisedValue = this._adaptableInstance.getNormalisedValueFromRawValue(rawValue, abColumn); if (normalisedValue) { const isoFormattedValue = DateFormatter(normalisedValue, { Pattern: `yyyy-MM-dd'T'HH:mm:ss.SSS`, }); if (isoFormattedValue) { finalCellExcelStyle.dataType = 'DateTime'; finalCellExcelStyle.numberFormat = { format: customDateFormatPattern }; const cellKey = AgGridExportAdapter.getExcelClassNameForCell(column.getColId(), this.adaptableApi.gridApi.getPrimaryKeyValueForRowNode(node)); this.registerExcelStyleWithFormattedDate(cellKey, isoFormattedValue); } } } } this.registerExcelStyle(finalCellExcelStyle, cellClassId); }); }, forAllVisibleRowNodesDoConfig); return Object.values(this.excelStylesCache); } registerExcelStyle(excelStyle, cellClassKey) { const excelStyleKey = JSON.stringify(excelStyle); if (!this.excelStylesCache[excelStyleKey]) { const excelStyleId = createUuid(); const excelStyleWithId = { ...excelStyle, id: excelStyleId }; this.excelStylesCache[excelStyleKey] = excelStyleWithId; } this.cellOrHeaderClassKey2ExcelStyleIdMap[cellClassKey] = this.excelStylesCache[excelStyleKey].id; } registerExcelStyleWithFormattedDate(cellClassId, isoFormattedValue) { this.excelStylesWithFormattedDate[cellClassId] = isoFormattedValue; } isDateCellExportedAsFormattedValue(abColumn, exportContext) { return (!!this.exportOptions.exportDateFormat || this.getCellExportFormatType(abColumn, 'date', exportContext) === 'formattedValue'); } convertCSSToExcelStyle(style) { const getHexColor = (color) => { const preparedColor = getVariableColor(color); const t = tinycolor(preparedColor); const a = t.getAlpha(); return tinycolor.mix(tinycolor('white'), t, a * 100).toHexString(); }; let result = {}; if (style.backgroundColor != null) { result.interior = { color: getHexColor(style.backgroundColor), pattern: 'Solid', }; } if (style.borderColor != null) { const excelBorder = { color: style.borderColor, lineStyle: 'Continuous', weight: 1, }; result.borders = { borderBottom: excelBorder, borderLeft: excelBorder, borderRight: excelBorder, borderTop: excelBorder, }; } if (style.textAlign) { result.alignment = { horizontal: StringExtensions.CapitaliseFirstLetter(style.textAlign), }; } if (style.color != null) { if (!result.font) { result.font = {}; } result.font = { color: getHexColor(style.color), }; } if (style.fontStyle === 'italic') { if (!result.font) { result.font = {}; } result.font.italic = true; } if (style.fontWeight != null && (style.fontWeight === 'bold' || Number(style.fontWeight) >= 600)) { if (!result.font) { result.font = {}; } result.font.bold = true; } if (style.fontSize != null) { if (!result.font) { result.font = {}; } result.font.size = convertCSSAbsoluteFontSizeToPt(style.fontSize); } return result; } getExcelDataType(adaptableColumnDataType) { switch (adaptableColumnDataType) { case 'number': return 'Number'; case 'boolean': return 'Boolean'; case 'date': return 'DateTime'; case 'text': default: return 'String'; } } getExcelStyleIdForClassKey(cellOrHeaderClassKey) { return this.cellOrHeaderClassKey2ExcelStyleIdMap[cellOrHeaderClassKey]; } getExcelStyleWithFormattedDate(cellClassId) { return this.excelStylesWithFormattedDate[cellClassId]; } mapCsvToJson(csvReportData, exportContext) { const columns = exportContext.exportedColumnIds .map((columnId) => this.adaptableApi.columnApi.getColumnWithColumnId(columnId)) .map((column) => ({ columnId: column.columnId, field: column.field ?? column.columnId, friendlyName: column.friendlyName, dataType: column.dataType, })); const reportData = { columns, rows: [], }; if (exportContext.report.ReportColumnScope === 'VisibleColumns') { if (exportContext.getCurrent().groupColumnIds?.length) { reportData.groupColumnIds = exportContext.getCurrent().groupColumnIds; } if (exportContext.getCurrent().pivotColumnIds?.length) { reportData.pivotColumnIds = exportContext.getCurrent().pivotColumnIds; } } const schema = inferSchema(csvReportData, { col: this.getCsvSeparator(exportContext), header: (rows) => { const headerRow = rows[0]; headerRow.forEach((headerName, index) => { const column = columns.find((col) => col.friendlyName === headerName); if (column) { headerRow[index] = column.columnId; } }); return [headerRow]; }, }); const parser = initParser(schema); const jsonData = parser.typedObjs(csvReportData); reportData.rows = jsonData; return reportData; } getCsvSeparator(exportProcessContext) { if (typeof this.exportOptions.csvSeparator === 'function') { return this.exportOptions.csvSeparator(this.adaptableApi.exportApi.internalApi.buildBaseExportContext(exportProcessContext.report.Name, exportProcessContext.format, exportProcessContext.destination)); } else { return this.exportOptions.csvSeparator; } } computeSkipColumnHeaders(exportProcessContext) { if (typeof this.exportOptions.skipColumnHeaders === 'function') { return this.exportOptions.skipColumnHeaders(this.adaptableApi.exportApi.internalApi.buildBaseExportContext(exportProcessContext.report.Name, exportProcessContext.format, exportProcessContext.destination)); } else { return this.exportOptions.skipColumnHeaders; } } computeGetCustomContentBelowRow(exportContext) { if (!this.adaptableApi.gridApi.isMasterDetailGrid()) { return; } const getDetailsRowsFn = this.exportOptions.getDetailRows; if (typeof getDetailsRowsFn !== 'function') { this.adaptableApi.logInfo('ExportOptions.getDetailRows() is not defined, skipping detail rows export'); return; } return (params) => { const { node } = params; const shouldExportDetailRows = exportContext.report.ReportRowScope === 'AllRows' || (exportContext.report.ReportRowScope === 'VisibleRows' && node.expanded); if (!shouldExportDetailRows) { return; } const context = { ...this.adaptableApi.exportApi.internalApi.buildBaseExportContext(exportContext.report.Name, exportContext.format, exportContext.destination), adaptableColumn: this.adaptableApi.columnApi.getColumnWithColumnId(params.column?.getId()), masterRowNode: node, masterRowData: node?.data, isExpanded: node.expanded, createCellCsv: (cellContent) => this.adaptableApi.exportApi.internalApi.createCellCsv(cellContent), createCellExcel: (cellContent, cellType) => this.adaptableApi.exportApi.internalApi.createCellExcel(cellContent, cellType), createCellHeader: (cellContent) => { return { ...this.adaptableApi.exportApi.internalApi.createCellHeader(cellContent), styleId: '_masterDetailHeader', }; }, }; const detailRows = getDetailsRowsFn(context); if (exportContext.isExcelReport) { const anyExcelRow = detailRows[0]; if (anyExcelRow.cells == undefined) { this.adaptableApi.logWarn(`Export ${exportContext.report.Name}: ExportOptions.getDetailRows() should return an array of ExcelRow objects`); return; } } else { const anyCsvCell = detailRows[0]?.[0]; if (anyCsvCell.data == undefined) { this.adaptableApi.logWarn(`Export ${exportContext.report.Name}: ExportOptions.getDetailRows() should return an array of CsvRow objects`); return; } } return detailRows; }; } getCellExportValueFromRawValue(rowNode, cellRawValue, columnId, exportContext) { if (StringExtensions.IsNullOrEmpty(cellRawValue)) { return cellRawValue; } const column = this.adaptableApi.columnApi.getColumnWithColumnId(columnId); const columnDataType = column.dataType; if (exportContext.isVisualExcelReport) { return this.getCellExportValueFromRawValueByType(rowNode, cellRawValue, columnId, 'formattedValue'); } const isDateColumn = columnDataType === 'date'; if (isDateColumn) { const customExportDateFormat = this.getCustomExportDateFormat(column, exportContext); if (!!customExportDateFormat) { return FormatHelper.DateFormatter(cellRawValue, { Pattern: customExportDateFormat, }); } } const cellExportFormat = this.getCellExportFormatType(column, columnDataType, exportContext); return this.getCellExportValueFromRawValueByType(rowNode, cellRawValue, columnId, cellExportFormat); } getCustomExportDateFormat(column, exportContext) { const exportDateFormatOption = this.adaptableApi.optionsApi.getExportOptions().exportDateFormat; return typeof exportDateFormatOption === 'function' ? exportDateFormatOption({ ...this.adaptableApi.exportApi.internalApi.buildBaseExportContext(exportContext.report.Name, exportContext.format, exportContext.destination), column, }) : exportDateFormatOption; } getCellExportValueFromRawValueByType(rowNode, cellRawValue, columnId, type = 'rawValue') { return type === 'rawValue' ? cellRawValue : this.adaptableApi.gridApi.getDisplayValueFromRawValue(rowNode, columnId, cellRawValue); } getCellExportFormatType(column, columnDataType, exportContext) { const exportDataFormat = this.exportOptions.exportDataFormat; if (exportDataFormat != null && typeof exportDataFormat === 'function') { const context = { ...this.adaptableApi.exportApi.internalApi.buildBaseExportContext(exportContext.report.Name, exportContext.format, exportContext.destination), column: column, }; return exportDataFormat(context); } if (exportDataFormat === 'rawValue') { return 'rawValue'; } if (exportDataFormat === 'formattedValue') { return 'formattedValue'; } const dataFormatDataType = exportDataFormat; if (dataFormatDataType) { switch (columnDataType) { case 'text': return dataFormatDataType.text; case 'number': return dataFormatDataType.number; case 'date': return dataFormatDataType.date; default: return 'rawValue'; } } return 'rawValue'; } }