@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
JavaScript
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,
};
}
}