UNPKG

@adaptabletools/adaptable

Version:

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

455 lines (454 loc) 20.6 kB
import { ApiBase } from '../Implementation/ApiBase'; import { createGrid, } from 'ag-grid-enterprise'; import { createUuid } from '../../AdaptableState/Uuid'; import { ALL_DATA_REPORT, CURRENT_LAYOUT_REPORT, SELECTED_DATA_REPORT, SYSTEM_EXPORT_DESTINATIONS, SYSTEM_REPORT_NAMES, } from '../../Utilities/Constants/GeneralConstants'; import StringExtensions from '../../Utilities/Extensions/StringExtensions'; import FormatHelper, { DateFormatter } from '../../Utilities/Helpers/FormatHelper'; import Helper from '../../Utilities/Helpers/Helper'; import * as PopupRedux from '../../Redux/ActionsReducers/PopupRedux'; import { SystemExportBegin, SystemExportEnd } from '../../Redux/ActionsReducers/InternalRedux'; export class ExportInternalApi extends ApiBase { /** * Value Items for Report Name Selection */ getAllAvailableReportNames() { return this.getExportApi() .getAllReports() .map((report) => report.Name); } isDataChangeInReport(cellDataChangedInfo, report) { // for All Data Report any data change is true so get out asap if (report.Name == 'All Data') { return true; } // Start with the DataChanged Column and go through all possibilities switch (report.ReportColumnScope) { case 'AllColumns': // has to be relevant so continue to rows break; case 'ScopeColumns': // use the Scope object which will tell us if the Column is relevant if (!this.getAdaptableApi().columnScopeApi.isColumnInScopeColumns(cellDataChangedInfo.column, report.Scope)) { return false; } break; case 'SelectedColumns': let selectedCellInfo = this.getAdaptableApi().gridApi.getSelectedCellInfo(); if (selectedCellInfo) { if (selectedCellInfo.columns.find((c) => c.columnId == cellDataChangedInfo.column.columnId) == null) { return false; } } break; case 'VisibleColumns': // hope that visibile column property is updated whenever the layout changes... (need to check!) if (!cellDataChangedInfo.column.visible) { return false; } break; } // now do rows switch (report.ReportRowScope) { case 'AllRows': return true; case 'ExpressionRows': return true; // TODO this is the only difficult one - do we check AdapTableQL? if we can then that is good as we can then do a delta after all... case 'SelectedRows': // CellSelection let selectedCellInfo = this.getAdaptableApi().gridApi.getSelectedCellInfo(); if (selectedCellInfo && selectedCellInfo.gridCells) { if (selectedCellInfo.gridCells.find((gc) => gc.primaryKeyValue == cellDataChangedInfo.primaryKeyValue)) { return true; } } // Row Selection let selectedRowInfo = this.getAdaptableApi().gridApi.getSelectedRowInfo(); if (selectedRowInfo && selectedRowInfo.gridRows) { if (selectedRowInfo.gridRows.find((gr) => gr.primaryKeyValue == cellDataChangedInfo.primaryKeyValue)) { return true; } } return false; case 'VisibleRows': return this.getGridApi().isVisibleRowNode(cellDataChangedInfo.rowNode); } } setExportInProgress(reportName, reportFormat, exportDestination) { this.dispatchAction(SystemExportBegin(reportName, reportFormat, exportDestination)); } setExportComplete() { this.dispatchAction(SystemExportEnd()); } isVisualDataExportInProgress() { return this.getAdaptableState().Internal.Export.inProgress?.reportFormat === 'VisualExcel'; } getCellExportFormatType(column, columnDataType) { const exportOptions = this.getExportOptions(); const exportDataFormat = exportOptions.exportDataFormat; // First check if a function was provided and return the result if (exportDataFormat != null && typeof exportDataFormat === 'function') { const context = { ...this.getAdaptableInternalApi().buildBaseContext(), column: column, }; return exportDataFormat(context); } // Next Check if a "hard-coded" value has been provided and return that if (exportDataFormat === 'rawValue') { return 'rawValue'; } if (exportDataFormat === 'formattedValue') { return 'formattedValue'; } // Finally test if a DataType object has been provided and return the relevant property const dataFormatDataType = exportDataFormat; if (dataFormatDataType) { // format is customized based on column data type switch (columnDataType) { case 'text': return dataFormatDataType.text; case 'number': return dataFormatDataType.number; case 'date': case 'dateString': return dataFormatDataType.date; default: // default to rawValue for all other column types return 'rawValue'; } } return 'rawValue'; } createSystemReport(systemReportName) { switch (systemReportName) { case ALL_DATA_REPORT: return { Uuid: createUuid(), Name: ALL_DATA_REPORT, ReportColumnScope: 'AllColumns', ReportRowScope: 'AllRows', Query: undefined, IsReadOnly: true, }; case CURRENT_LAYOUT_REPORT: return { Uuid: createUuid(), Name: CURRENT_LAYOUT_REPORT, ReportColumnScope: 'VisibleColumns', ReportRowScope: 'VisibleRows', Query: undefined, IsReadOnly: true, }; case SELECTED_DATA_REPORT: return { Uuid: createUuid(), Name: SELECTED_DATA_REPORT, ReportColumnScope: 'SelectedColumns', ReportRowScope: 'SelectedRows', Query: undefined, IsReadOnly: true, }; } } isSystemReport(reportName) { return SYSTEM_REPORT_NAMES.includes(reportName); } isSystemDestination(destination) { return SYSTEM_EXPORT_DESTINATIONS.includes(destination); } isSystemReportActive(reportName) { if (reportName == SELECTED_DATA_REPORT) { return (this.getAdaptableApi().gridApi.isGridRangeSelectable() || this.getAdaptableApi().gridApi.isGridRowSelectable()); } return true; } getReportColumnScopeShortDescription(report) { switch (report.ReportColumnScope) { case 'AllColumns': return ['[All Columns]']; case 'VisibleColumns': return ['[Visible Columns]']; case 'SelectedColumns': return ['[Selected Columns]']; case 'ScopeColumns': if ('ColumnIds' in report?.Scope) { return report.Scope.ColumnIds.map?.((columnId) => this.getAdaptableApi().columnApi.getFriendlyNameForColumnId(columnId) ?? columnId); } return ['[Bespoke Columns]']; } } getReportColumnScopeLongDescription(report) { switch (report.ReportColumnScope) { case 'AllColumns': return '[All Columns]'; case 'VisibleColumns': return '[Visible Columns]'; case 'SelectedColumns': return '[Selected Columns]'; case 'ScopeColumns': return this.getAdaptableApi().columnScopeApi.getScopeDescription(report.Scope); } } getReportExpressionDescription(report, cols) { if (this.isSystemReport(report.Name)) { return `[${report.Name}]`; } else { switch (report.ReportRowScope) { case 'AllRows': return '[All Rows]'; case 'VisibleRows': return '[Visible Rows]'; case 'SelectedRows': return '[Selected Rows]'; case 'ExpressionRows': return this.getAdaptableInternalApi().getAdaptableQueryExpressionText(report.Query); } } } convertReportDataToArray(reportData) { return [ reportData.columns.map((column) => column.friendlyName), ...reportData.rows.map((row) => reportData.columns.map((column) => row[column.field ?? column.columnId])), ]; } publishLiveLiveDataChangedEvent(reportDestination, liveDataTrigger, liveReport) { const liveDataChangedInfo = { ...this.getAdaptableInternalApi().buildBaseContext(), reportDestination: reportDestination, liveDataTrigger: liveDataTrigger, liveReport: liveReport, }; this.getAdaptableApi().eventApi.emit('LiveDataChanged', liveDataChangedInfo); } getCellExportValueFromRowNode(rowNode, columnId, isVisualReport) { return this.getCellExportValueFromRawValue(rowNode, this.getAdaptableApi().gridApi.getRawValueFromRowNode(rowNode, columnId), columnId, isVisualReport); } getCellExportValueFromRawValue(rowNode, cellRawValue, columnId, isVisualReport) { if (StringExtensions.IsNullOrEmpty(cellRawValue)) { return cellRawValue; } const column = this.getAdaptableApi().columnApi.getColumnWithColumnId(columnId); const columnDataType = column.dataType; // 1. if it is a VisualExcel report format, we always ONLY send the formatted value and ignore all other properties if (isVisualReport) { return this.getCellExportValueFromRawValueByType(rowNode, cellRawValue, columnId, 'formattedValue'); } const isDateColumn = columnDataType === 'date' || columnDataType === 'dateString'; // 2. if this is a date column and there is a custom export date format provided, that will next take precedence if (isDateColumn && !!this.getCustomExportDateFormat()) { const exportDateFormat = this.getCustomExportDateFormat(); return FormatHelper.DateFormatter(cellRawValue, { Pattern: exportDateFormat, }); } // 3. in all other cases check the general export format types const cellExportFormat = this.getAdaptableApi().exportApi.internalApi.getCellExportFormatType(column, columnDataType); return this.getCellExportValueFromRawValueByType(rowNode, cellRawValue, columnId, cellExportFormat); } getReportFileName(reportName, reportFormat, destination) { let fileName = StringExtensions.ReplaceEmptySpacesWithUnderscore(`${reportName}-${reportFormat}`); const reportFilename = this.getAdaptableApi().optionsApi.getExportOptions().reportFilename; if (reportFilename) { const reportFileNameContext = { fileName, ...this.buildBaseExportContext(reportName, reportFormat, destination), }; fileName = reportFilename(reportFileNameContext); } else { if (this.getAdaptableApi().optionsApi.getExportOptions().appendFileTimestamp) { fileName = `${fileName}_${DateFormatter(new Date(), { Pattern: 'yyyyMMdd_HHmmss', })}`; } } return fileName; } getCustomExportDateFormat() { return this.getAdaptableApi().optionsApi.getExportOptions().exportDateFormat; } getCellExportValueFromRawValueByType(rowNode, cellRawValue, columnId, // default to rawValue if, for some reason, the configs provide invalid values type = 'rawValue') { return type === 'rawValue' ? cellRawValue : // type === formattedValue this.getAdaptableApi().gridApi.getDisplayValueFromRawValue(rowNode, columnId, cellRawValue); } sendReportToDestination(reportResult, report, format, destination) { if (!reportResult?.data) { this.logWarn(`No report result to send to destination for report '${report.Name}, format '${format}' and destination '${destination}'`); return; } if (this.getExportApi().isExportDestinationCustom(destination)) { this.sendReportToCustomDestination(reportResult, report, format, destination); } if (destination === 'Download') { if (reportResult.type === 'excel') { Helper.createDownloadedFile(reportResult.data, this.getReportFileName(report.Name, format, destination), 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); } if (reportResult.type === 'csv') { Helper.createDownloadedFile(reportResult.data, this.getReportFileName(report.Name, format, destination), 'text/csv;encoding:utf-8'); } if (reportResult.type === 'json') { Helper.createDownloadedFile(JSON.stringify(reportResult.data), this.getReportFileName(report.Name, format, destination), 'application/json'); } } if (destination === 'Clipboard') { if (reportResult.type !== 'csv' && reportResult.type !== 'json') { this.logWarn(`Cannot copy to clipboard as report result is not a string (report '${report.Name}', format '${format}', destination '${destination}')`); return; } Helper.copyToClipboard(reportResult.type === 'json' ? JSON.stringify(reportResult.data) : reportResult.data); } } sendReportToCustomDestination(reportResult, report, format, destination) { const customDestination = this.getOptionsApi() .getAdaptableOptions() .exportOptions.customDestinations?.find((cd) => cd.name == destination); if (customDestination?.form) { this.getAdaptableInternalApi().dispatchReduxAction(PopupRedux.PopupShowForm({ Id: 'export-destination-form', Form: customDestination?.form, prepareContext: (context) => { // we want to give the current popup time to close // and we reopen with a delay in case this button action causes another popup return new Promise((resolve) => { setTimeout(() => { const preparedContext = { ...context, report, customDestination, reportData: reportResult, }; resolve(preparedContext); }, 20); }); }, })); } else if (customDestination) { const reportContext = { report: report, reportData: reportResult, ...this.buildBaseExportContext(report.Name, format, destination), }; customDestination.onExport(reportContext); } } buildBaseExportContext(reportName, reportFormat, exportDestination) { const report = this.getExportApi().getReportByName(reportName); return { ...this.getAdaptableInternalApi().buildBaseContext(), report, reportName, reportFormat, exportDestination, }; } createCellCsv(cellContent) { return { data: { value: cellContent != undefined ? String(cellContent) : null, }, }; } createCellExcel(cellContent, cellType) { return { data: { value: cellContent != undefined ? String(cellContent) : null, type: cellType, }, }; } createCellHeader(cellContent) { return { data: { value: cellContent != undefined ? String(cellContent) : null, type: 'String', }, }; } buildProcessExportContext(report, format, destination) { const { exportContext } = this._adaptable.agGridExportAdapter.buildExportProcessData({ report, format, destination, showProgressIndicator: false, }); return { ...this.buildBaseExportContext(report.Name, format, destination), convertToExcel: this.buildExcelConverter(report.Name, format, destination), convertToCsv: this.buildCsvConverter(report.Name, format, destination), getReportColumns: () => exportContext.exportedColumnIds .map((colId) => this.getAdaptableApi().columnApi.getColumnWithColumnId(colId, false)) .filter(Boolean), }; } buildExcelConverter(reportName, reportFormat, exportDestination) { return (reportData) => { return this.executeGridExport(reportData, reportName, reportFormat, exportDestination, (gridApi, exportParams) => gridApi.getDataAsExcel({ ...exportParams })); }; } buildCsvConverter(reportName, reportFormat, exportDestination) { return (reportData) => { const csvParams = this.buildCsvExportParams(reportName, reportFormat, exportDestination); return this.executeGridExport(reportData, reportName, reportFormat, exportDestination, (gridApi, exportParams) => gridApi.getDataAsCsv({ ...exportParams, ...csvParams })); }; } executeGridExport(reportData, reportName, reportFormat, exportDestination, exportFn) { const htmlDivElement = document.createElement('div'); let ephemeralGridApi = null; try { const columnDefs = reportData.columns.map((col) => ({ colId: col.columnId, field: col.field ?? col.columnId, headerName: col.friendlyName ?? col.columnId, cellDataType: col.dataType ?? 'text', })); const gridOptions = { columnDefs, rowData: reportData.rows, theme: this.getAgGridApi().getGridOption('theme'), dataTypeDefinitions: this.getAgGridApi().getGridOption('dataTypeDefinitions'), }; const gridParams = { modules: this.getAdaptableApi() .internalApi.getAdaptableInstance() .getAgGridRegisteredModules(), }; htmlDivElement.style.display = 'none'; document.body.appendChild(htmlDivElement); ephemeralGridApi = createGrid(htmlDivElement, gridOptions, gridParams); const exportParams = { fileName: this.getReportFileName(reportName, reportFormat, exportDestination), allColumns: true, exportedRows: 'all', }; return exportFn(ephemeralGridApi, exportParams); } catch (error) { this.logWarn('Failed to export data:', error); return null; } finally { if (ephemeralGridApi) { ephemeralGridApi.destroy(); } if (htmlDivElement?.parentNode) { document.body.removeChild(htmlDivElement); } } } buildCsvExportParams(reportName, reportFormat, exportDestination) { const exportOptions = this.getAdaptableApi().optionsApi.getExportOptions(); const csvSeparator = typeof exportOptions.csvSeparator === 'function' ? exportOptions.csvSeparator(this.buildBaseExportContext(reportName, reportFormat, exportDestination)) : exportOptions.csvSeparator; return { columnSeparator: csvSeparator, }; } }